package com.xbongbong.saas.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.ConfigConstant;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.PaasFormDataEntity;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.PaasProcessDataEntity;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormEntityExt;
import com.xbongbong.paas.elasticsearch.constant.EsNormalConstant;
import com.xbongbong.paas.elasticsearch.model.PaasEsModel;
import com.xbongbong.paas.elasticsearch.pojo.XbbAggregatedPage;
import com.xbongbong.paas.elasticsearch.util.EsHelper;
import com.xbongbong.paas.elasticsearch.util.EsUtil;
import com.xbongbong.paas.elasticsearch.util.XbbElasticsearchRestTemplate;
import com.xbongbong.paas.enums.DefaultTypeEnum;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.enums.FundAccountFlowTypeEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.enums.PaymentSheetTypeEnum;
import com.xbongbong.paas.enums.PlatFormEnum;
import com.xbongbong.paas.enums.SheetTypeEnum;
import com.xbongbong.paas.enums.UnitEnum;
import com.xbongbong.paas.field.DefaultAttrPoJo;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.help.CommonHelp;
import com.xbongbong.paas.help.FormDataValidateDataHelp;
import com.xbongbong.paas.help.ProSaveHelp;
import com.xbongbong.paas.help.TransactionHelp;
import com.xbongbong.paas.log.MongoLogHelp;
import com.xbongbong.paas.log.constant.LogMemoConstant;
import com.xbongbong.paas.log.pojo.InfoArrPojo;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.model.PaasFormModel;
import com.xbongbong.paas.model.PaasProcessDataModel;
import com.xbongbong.paas.model.PaasProcessTemplateModel;
import com.xbongbong.paas.pojo.AddMuchFieldPojo;
import com.xbongbong.paas.pojo.AttrValuePojo;
import com.xbongbong.paas.pojo.CreditCardBillInfoPojo;
import com.xbongbong.paas.pojo.FieldAttrEntityForImport;
import com.xbongbong.paas.pojo.FormTitlePoJo;
import com.xbongbong.paas.pojo.SummaryDataPoJo;
import com.xbongbong.paas.pojo.dto.AddBatchDTO;
import com.xbongbong.paas.pojo.dto.CreditCardBillDTO;
import com.xbongbong.paas.pojo.dto.FormDataAddDTO;
import com.xbongbong.paas.pojo.dto.FormDataDeleteBatchDTO;
import com.xbongbong.paas.pojo.dto.FormDataListDTO;
import com.xbongbong.paas.pojo.dto.FormDataUpdateDTO;
import com.xbongbong.paas.pojo.dto.ImportFormDataDTO;
import com.xbongbong.paas.pojo.dto.SaasFormSaveDTO;
import com.xbongbong.paas.pojo.dto.StartProcessDTO;
import com.xbongbong.paas.pojo.dto.ValidateDataDTO;
import com.xbongbong.paas.pojo.imports.CellTitlePojo;
import com.xbongbong.paas.pojo.imports.CellValuePojo;
import com.xbongbong.paas.pojo.imports.RowGroupPojo;
import com.xbongbong.paas.pojo.vo.FormDataAddVO;
import com.xbongbong.paas.pojo.vo.StartProcessVO;
import com.xbongbong.paas.service.PaasFormExplainService;
import com.xbongbong.paas.service.WorkflowOperatorDataService;
import com.xbongbong.paas.service.dynamic.crm.strategy.DynamicStrategyFactory;
import com.xbongbong.paas.service.dynamic.crm.strategy.PaymentSheetDynamicStrategy;
import com.xbongbong.paas.service.team.TeamDataHelp;
import com.xbongbong.paas.service.workflow.WorkflowDataService;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.DateUtil;
import com.xbongbong.paas.toolbox.util.JsonHelperUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.toolbox.wrap.UserVO;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.api.pojo.DataInfoDTO;
import com.xbongbong.pro.applist.pojo.AppListPojo;
import com.xbongbong.pro.applist.pojo.vo.ListAppVO;
import com.xbongbong.pro.businessdata.pojo.dto.FormatExplainDTO;
import com.xbongbong.pro.constant.CustomerCommunicateConstant;
import com.xbongbong.pro.constant.ElasticConstant;
import com.xbongbong.pro.constant.I18nStringConstant;
import com.xbongbong.pro.constant.SymbolConstant;
import com.xbongbong.pro.customercommunicate.pojo.dto.CustomerDynamicAddDTO;
import com.xbongbong.pro.domain.entity.UpdateDataEntity;
import com.xbongbong.pro.dynamic.pojo.dto.DynamicDeleteDTO;
import com.xbongbong.pro.dynamic.pojo.dto.DynamicPaymentSheetDTO;
import com.xbongbong.pro.enums.DistributorMarkEnum;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.errorcodes.ContractErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.InvoiceErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.PaymentErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.TemplateErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.UserTeamErrorCodeEnum;
import com.xbongbong.pro.formdata.pojo.AfterSavePojo;
import com.xbongbong.pro.formdata.pojo.SaasNeedRedundantAttrPojo;
import com.xbongbong.pro.formexplain.pojo.dto.HandlerExplainInLinkItemDTO;
import com.xbongbong.pro.listbatch.pojo.dto.AddLabelDTO;
import com.xbongbong.pro.message.constant.PaymentPushConstant;
import com.xbongbong.pro.message.enums.PushTypeEnum;
import com.xbongbong.pro.message.help.PushHelper;
import com.xbongbong.pro.message.pojo.PushRefTypePojo;
import com.xbongbong.pro.message.pojo.dto.MessageRabbitMqDTO;
import com.xbongbong.pro.paymentsheet.pojo.dto.PaymentSheetDeleteBatchDTO;
import com.xbongbong.pro.paymentsheet.pojo.dto.PaymentSheetEditAttrUpdateDTO;
import com.xbongbong.pro.paymentsheet.pojo.dto.PaymentSheetGetDTO;
import com.xbongbong.pro.paymentsheet.pojo.dto.PaymentSheetInsertBatchDTO;
import com.xbongbong.pro.paymentsheet.pojo.dto.PaymentSheetInsertDTO;
import com.xbongbong.pro.paymentsheet.pojo.dto.PaymentSheetSaveDTO;
import com.xbongbong.pro.paymentsheet.pojo.dto.PaymentSheetUpdateBatchDTO;
import com.xbongbong.pro.paymentsheet.pojo.dto.PaymentSheetUpdateDTO;
import com.xbongbong.pro.paymentsheet.pojo.dto.batch.PaymentSheetDTO;
import com.xbongbong.pro.paymentsheet.pojo.dto.batch.PaymentSheetInsertBatchByContractDTO;
import com.xbongbong.pro.paymentsheet.pojo.dto.batch.PaymentSheetUpdateBatchMuchFieldDTO;
import com.xbongbong.pro.paymentsheet.pojo.vo.PaymentSheetDeleteBatchVO;
import com.xbongbong.pro.paymentsheet.pojo.vo.PaymentSheetGetVO;
import com.xbongbong.pro.paymentsheet.pojo.vo.PaymentSheetInsertBatchVO;
import com.xbongbong.pro.paymentsheet.pojo.vo.PaymentSheetUpdateBatchVO;
import com.xbongbong.pro.paymentsheet.pojo.vo.batch.PaymentSheetAmountVO;
import com.xbongbong.pro.paymentsheet.pojo.vo.batch.PaymentSheetInsertBatchByContractVO;
import com.xbongbong.pro.paymentsheet.pojo.vo.batch.PaymentSheetUpdateBatchMuchFieldVO;
import com.xbongbong.pro.rabbitmq.producer.BaseProducer;
import com.xbongbong.pro.service.toolbox.elasticsearch.help.ProEsHelper;
import com.xbongbong.pro.service.toolbox.statistic.help.FormHelp;
import com.xbongbong.pro.service.toolbox.statistic.help.SystemChartHelp;
import com.xbongbong.pro.sheet.pojo.AmountDetailGetPojo;
import com.xbongbong.pro.sheet.pojo.SheetAttrPojo;
import com.xbongbong.pro.sheet.pojo.dto.SheetAmountDetailGetDTO;
import com.xbongbong.pro.sheet.pojo.dto.SheetDeleteHandleRelateDTO;
import com.xbongbong.pro.sheet.pojo.vo.HandleLinkInfoVO;
import com.xbongbong.pro.sheet.pojo.vo.SheetAmountDetailGetVO;
import com.xbongbong.pro.thread.ThreadPoolBeanConfig;
import com.xbongbong.pro.user.pojo.ReturnUserAndDepartmentPojo;
import com.xbongbong.process.operate.ProcessStartHelp;
import com.xbongbong.saas.analytical.impl.PaymentSheetValidateAnalyticalServiceImpl;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ErrorMessageConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.DistributorBaseInfoEntity;
import com.xbongbong.saas.domain.entity.FundAccountFlowEntity;
import com.xbongbong.saas.domain.entity.OnlinePaymentFlowEntity;
import com.xbongbong.saas.domain.entity.PaymentBalanceEntity;
import com.xbongbong.saas.domain.entity.PaymentSheetUserEntity;
import com.xbongbong.saas.domain.entity.UserTeamEntity;
import com.xbongbong.saas.domain.entity.ext.PaymentSheetEntityExt;
import com.xbongbong.saas.enums.CreditCardBillTypeEnum;
import com.xbongbong.saas.enums.LinkAddOperationFlagEnum;
import com.xbongbong.saas.enums.OnlinePaymentFlowDistributorEnum;
import com.xbongbong.saas.enums.OperateModuleTypeEnum;
import com.xbongbong.saas.enums.OperateTypeEnum;
import com.xbongbong.saas.enums.ProjectLogEnum;
import com.xbongbong.saas.enums.RedundantTemplateTypeEnum;
import com.xbongbong.saas.enums.SheetAloneEnum;
import com.xbongbong.saas.enums.SheetAssembleEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.base.PaymentSheetBaseEnum;
import com.xbongbong.saas.enums.business.ContractEnum;
import com.xbongbong.saas.enums.business.CustomerManagementEnum;
import com.xbongbong.saas.enums.business.InvoiceEnum;
import com.xbongbong.saas.enums.business.PaymentEnum;
import com.xbongbong.saas.enums.business.PaymentSheetDistributorEnum;
import com.xbongbong.saas.enums.business.PaymentSheetEnum;
import com.xbongbong.saas.enums.business.PaymentTaskEnum;
import com.xbongbong.saas.help.FundHelp;
import com.xbongbong.saas.help.ImportHelper;
import com.xbongbong.saas.help.LogHandleHelp;
import com.xbongbong.saas.help.SaasCreditHelper;
import com.xbongbong.saas.help.SaasCustomerStatementHelper;
import com.xbongbong.saas.help.SaasDataInitHelp;
import com.xbongbong.saas.help.SaasDeleteHelp;
import com.xbongbong.saas.help.SaasListHelp;
import com.xbongbong.saas.help.SaasPaymentHelper;
import com.xbongbong.saas.help.SaasPaymentSheetHelper;
import com.xbongbong.saas.help.SaasRefundHelper;
import com.xbongbong.saas.help.SaasSaveHelp;
import com.xbongbong.saas.help.SaasUpdateHelp;
import com.xbongbong.saas.help.fund.PaymentSheetHelp;
import com.xbongbong.saas.help.workflow.ContractHelp;
import com.xbongbong.saas.help.workflow.PayAndPaymentSheetHelp;
import com.xbongbong.saas.model.ContractModel;
import com.xbongbong.saas.model.FundAccountFlowModel;
import com.xbongbong.saas.model.OnlinePaymentFlowModel;
import com.xbongbong.saas.model.PaymentBalanceModel;
import com.xbongbong.saas.model.PaymentModel;
import com.xbongbong.saas.model.PaymentSheetModel;
import com.xbongbong.saas.model.PaymentSheetUserModel;
import com.xbongbong.saas.service.ContractService;
import com.xbongbong.saas.service.CreditCardBillService;
import com.xbongbong.saas.service.CustomerCommunicateService;
import com.xbongbong.saas.service.CustomerStatementService;
import com.xbongbong.saas.service.FundAccountFlowService;
import com.xbongbong.saas.service.FundAccountService;
import com.xbongbong.saas.service.FundSetService;
import com.xbongbong.saas.service.InvoiceService;
import com.xbongbong.saas.service.PaymentBalanceService;
import com.xbongbong.saas.service.PaymentService;
import com.xbongbong.saas.service.PaymentSheetService;
import com.xbongbong.saas.service.RefundService;
import com.xbongbong.saas.service.UserTeamService;
import com.xbongbong.saas.service.toolbox.help.LogHelp;
import com.xbongbong.saas.service.toolbox.help.SaasSaveHelper;
import com.xbongbong.saas.service.toolbox.help.UserTeamHelp;
import com.xbongbong.saas.toolbox.help.FastJsonHelper;
import com.xbongbong.sys.domain.entity.UserEntity;
import com.xbongbong.sys.model.UserModel;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

import static com.xbongbong.saas.toolbox.help.FastJsonHelper.getDoubleOrDefaultFromFormData;
import static java.lang.String.format;
import static java.lang.String.join;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;

/**
 * @author 吴峰
 * @version v1.0
 * @date 2019/2/23 16:53
 * @since v1.0
 */
@Service("paymentSheetService")
public class PaymentSheetServiceImpl implements PaymentSheetService {

    private static final Logger LOG = LoggerFactory.getLogger(PaymentSheetServiceImpl.class);

    @Resource
    private PaymentSheetModel paymentSheetModel;
    @Resource
    private MongoLogHelp mongoLogHelp;
    @Resource
    private SaasUpdateHelp saasUpdateHelp;
    @Resource
    private UserModel userModel;
    @Resource
    private PaymentModel paymentModel;
    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private PaasFormModel paasFormModel;
    @Resource
    private PaymentService paymentService;
    @Resource
    private XbbElasticsearchRestTemplate xbbElasticsearchRestTemplate;
    @Resource
    private PushHelper pushHelper;
    @Resource
    private BaseProducer baseProducer;
    @Resource
    private ProcessStartHelp processStartHelp;
    @Resource
    private EsHelper esHelper;
    @Resource
    private ContractModel contractModel;
    @Resource
    private ContractService contractService;
    @Resource
    private FormDataValidateDataHelp formDataValidateDataHelp;
    @Resource
    @Lazy
    private CustomerCommunicateService customerCommunicateService;
    @Resource
    private ProEsHelper proEsHelper;
    @Resource
    private SaasSaveHelp saasSaveHelp;
    @Resource
    private SaasSaveHelper saasSaveHelper;
    @Resource
    private PaymentBalanceService paymentBalanceService;
    @Resource
    private PaymentBalanceModel paymentBalanceModel;
    @Resource
    private CommonHelp commonHelp;
    @Resource
    private FormHelp formHelp;
    @Resource
    private PaasEsModel paasEsModel;
    @Resource
    private SystemChartHelp systemChartHelp;
    @Resource
    private ImportHelper importHelper;
    @Resource
    private RefundService refundService;
    @Resource
    private InvoiceService invoiceService;
    @Resource
    private PaasProcessDataModel paasProcessDataModel;
    @Resource
    private FundHelp fundHelp;
    @Resource
    private FundAccountService fundAccountService;
    @Resource
    private SaasListHelp saasListHelp;
    @Resource
    private FundAccountFlowModel fundAccountFlowModel;
    @Resource
    private FundAccountFlowService fundAccountFlowService;
    @Resource
    private UserTeamService userTeamService;
    @Resource
    private PaymentSheetUserModel paymentSheetUserModel;
    @Resource
    private PaasProcessTemplateModel paasProcessTemplateModel;
    @Resource
    private ThreadPoolBeanConfig threadPoolBeanConfig;
    @Resource
    private PaymentSheetHelp paymentSheetHelp;
    @Resource
    private LogHelp logHelp;
    @Resource
    private LogHandleHelp logHandleHelp;
    @Resource
    private CustomerStatementService customerStatementService;
    @Resource
    private FundSetService fundSetService;
    @Resource
    private PaasFormExplainService paasFormExplainService;
    @Resource
    private CreditCardBillService creditCardBillService;
    @Resource
    private OnlinePaymentFlowModel onlinePaymentFlowModel;
    @Resource
    private PaymentSheetValidateAnalyticalServiceImpl paymentSheetAnalyticalService;
    @Resource
    private SaasPaymentHelper saasPaymentHelper;
    @Resource
    private ContractHelp contractHelp;
    @Resource
    private SaasPaymentSheetHelper saasPaymentSheetHelper;
    @Resource
    private PayAndPaymentSheetHelp payAndPaymentSheetHelp;
    @Resource
    private SaasCustomerStatementHelper saasCustomerStatementHelper;
    @Resource
    private SaasCreditHelper saasCreditHelper;
    @Resource
    private WorkflowOperatorDataService workflowOperatorDataService;
    @Resource
    private WorkflowDataService workflowDataService;
    @Resource
    private SaasRefundHelper saasRefundHelper;
    @Resource
    private TeamDataHelp teamDataHelp;
    @Resource
    private UserTeamHelp userTeamHelp;
    @Resource
    private DynamicStrategyFactory dynamicStrategyFactory;
    @Resource
    private TransactionHelp transactionHelp;

    @Deprecated
    public PaymentSheetInsertBatchVO insertBatch(PaymentSheetInsertBatchDTO paymentSheetInsertBatchDTO, String processCreatorId) throws XbbException {
        PaymentSheetInsertBatchVO paymentSheetInsertBatchVO = new PaymentSheetInsertBatchVO();
        try {
            String corpid = paymentSheetInsertBatchDTO.getCorpid();
            List<PaymentSheetInsertDTO> paymentSheetDTOList = paymentSheetInsertBatchDTO.getPaymentSheetList();
            PaasFormExplainEntity sheetExplain = paasFormExplainModel.getByBusinessType4Distributor(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), corpid, paymentSheetInsertBatchDTO.getDistributorMark());
            Long formId = sheetExplain.getFormId();
            List<FundAccountFlowEntity> fundAccountFlowEntities = new ArrayList<>();
            Map<Long, Double> fundAccountToAmountMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            if (!paymentSheetDTOList.isEmpty()) {
                List<PaymentSheetEntityExt> paymentSheetList = new ArrayList<>();
                paymentSheetDTOList.forEach((item)->{
                    PaymentSheetEntityExt paymentSheetEntityExt = new PaymentSheetEntityExt();
                    paymentSheetEntityExt.setDepartmentId(1L);
                    paymentSheetEntityExt.setFlowStatus(0);
                    BeanUtil.copyProperties(item, paymentSheetEntityExt);
                    paymentSheetEntityExt.setCorpid(corpid);
                    if (processCreatorId != null) {
                        paymentSheetEntityExt.setCreatorId(processCreatorId);
                    } else {
                        paymentSheetEntityExt.setCreatorId(paymentSheetInsertBatchDTO.getUserId());
                    }
                    long now = DateUtil.getInt();
                    paymentSheetEntityExt.setSerialNo(item.getSerialNo());
                    paymentSheetEntityExt.setAddTime(now);
                    paymentSheetEntityExt.setUpdateTime(now);
                    paymentSheetEntityExt.setData(item.getData());
                    paymentSheetEntityExt.setFormId(formId);
                    paymentSheetEntityExt.setUuid(UUID.randomUUID().toString());
                    paymentSheetEntityExt.setAlone(SheetAloneEnum.NORMAL.getCode());
                    paymentSheetList.add(paymentSheetEntityExt);
                });
                List<PaymentSheetEntityExt>  insertPaymentSheetList = paymentSheetModel.insertBatch(paymentSheetList);
                if ( CollectionsUtil.isNotEmpty(insertPaymentSheetList) ) {
                    List<Long> contractIdIn = new ArrayList<>();
                    Map<Long,List<Long>> idAndContractIdMap = new HashMap<>(paymentSheetList.size());
                    JSONObject customerData =  paymentSheetList.get(0).getData();
                    Long customerId  = FastJsonHelper.getLongOrDefaultFromFormData(customerData,PaymentSheetEnum.CUSTOMER_ID.getAttr(),0L);
                    for (PaymentSheetEntityExt paymentEntityExt : insertPaymentSheetList){
                        JSONObject data = paymentEntityExt.getData();
                        fundHelp.handleSheetIdAndContractIdMap(contractIdIn, idAndContractIdMap, paymentEntityExt, PaymentSheetEnum.CONTRACT.getAttr());
                        Long accountId = FastJsonHelper.getLongOrDefaultFromFormData(data, PaymentSheetEnum.FUND_ACCOUNT.getAttr(), 0L);
                        Long sheetTime = FastJsonHelper.getLongOrDefaultFromFormData(data, PaymentSheetEnum.PAYMENT_TIME.getAttr(), DateUtil.getNow());
                        Double sheetAmount = getDoubleOrDefaultFromFormData(data, PaymentSheetEnum.AMOUNT.getAttr(), 0D);
                        //账户余额变动
                        fundAccountToAmountMap.merge(accountId, sheetAmount, Arith::add);
                        //流水表日志记录
                        FundAccountFlowEntity fundAccountFlowEntity = new FundAccountFlowEntity(corpid, DateTimeUtil.getInt(), paymentEntityExt.getCreatorId(), FundAccountFlowTypeEnum.PAYMENT_SHEET.getCode(), paymentEntityExt.getId(), accountId, sheetTime, sheetAmount);
                        fundAccountFlowEntities.add(fundAccountFlowEntity);
                    }
                    if (fundAccountFlowEntities.size() > 0) {
                        fundAccountFlowModel.insertBatch(fundAccountFlowEntities);
                    }
                    if (fundAccountToAmountMap.size() > 0) {
                        fundAccountService.saveBatchByPaymentSheetOrPaySheet(fundAccountToAmountMap);
                    }
                    userTeamService.createPaymentSheetUser(corpid,customerId,contractIdIn,idAndContractIdMap,SheetTypeEnum.WRITE_OFF_PREPAYMENT,false);
                }
            }
        } catch (Exception e) {
            LOG.error("paymentSheetServiceImpl.insertBatch批量添加回款单出错：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return paymentSheetInsertBatchVO;
    }
    /**
     * 获取回款团队
     *
     * @param customerId
     * @param contractId
     * @param paymentUserEntityList
     * @param paymentId
     * @param entity
     * @param corpid
     */
    private void getPaymentSheetUserTeam(Long customerId, Long contractId, List<PaymentSheetUserEntity> paymentUserEntityList, Long paymentId, ReturnUserAndDepartmentPojo entity, Integer isMain, String corpid) {
        PaymentSheetUserEntity paymentSheetUserEntity = new PaymentSheetUserEntity();
        paymentSheetUserEntity.setCorpid(corpid);
        paymentSheetUserEntity.setIsMain(isMain);
        paymentSheetUserEntity.setUserId(entity.getId().toString());
        paymentSheetUserEntity.setUserName(entity.getName());
        paymentSheetUserEntity.setId(null);
        paymentSheetUserEntity.setDataId(paymentId);
        paymentUserEntityList.add(paymentSheetUserEntity);
    }

    @Override
    public PaymentSheetDeleteBatchVO deleteBatch(PaymentSheetDeleteBatchDTO paymentSheetDeleteBatchDTO, DelEnum delEnum) throws XbbException {
        PaymentSheetDeleteBatchVO paymentSheetDeleteBatchVO = new PaymentSheetDeleteBatchVO();
        List<Long> dataIdList = paymentSheetDeleteBatchDTO.getIdIn();
        if (dataIdList.size() > BasicConstant.MAX_DEL_BATCH_COUNT) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100079, SystemErrorCodeEnum.API_ERROR_100079.getMsg(), BasicConstant.MAX_DEL_BATCH_COUNT);
        }
        String corpid = paymentSheetDeleteBatchDTO.getCorpid();
        try {
            List<PaasFormDataEntityExt> paymentSheetList = saasListHelp.getPaasFormDataList4Sheet(dataIdList, corpid, XbbRefTypeEnum.PAYMENT_SHEET, IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET);
            deletePaymentSheet(delEnum, paymentSheetDeleteBatchDTO, paymentSheetDeleteBatchVO, paymentSheetList, corpid, paymentSheetDeleteBatchDTO.getSingleFlag());
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("paymentSheetServiceImpl.deleteBatch 批量删除回款单出错：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return paymentSheetDeleteBatchVO;
    }

    @Override
    public void updateRollback(Collection<Long> idIn, String corpid) throws XbbException {
        if (Objects.isNull(idIn) || idIn.isEmpty()) {
            return;
        }
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put(StringConstant.CORPID, corpid);
        param.put("idIn", idIn);
        List<PaymentSheetEntityExt> paymentSheetList = paymentSheetModel.findEntitysWithoutSub(param);
        if (paymentSheetList == null || paymentSheetList.size() == 0) {
            return;
        }
        for (PaymentSheetEntityExt item : paymentSheetList) {
            if (Objects.isNull(item.getId())) {
                continue;
            }
            PaasFormDataEntity paasFormDataEntity = new PaasFormDataEntity();
            BeanUtil.copyProperties(item, paasFormDataEntity);
            proEsHelper.reductionEsInUpdate(paasFormDataEntity, item.getId(), corpid, IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET);
        }
    }

    /**
     * 新增失败的回滚-直接删除es
     * @param paymentSheetList
     * @param corpid
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void addRollbackList(List<PaymentSheetEntityExt> paymentSheetList, String corpid) throws XbbException {
        if (paymentSheetList == null || paymentSheetList.size() == 0) {
            return;
        }
        IndexTypeEnum indexTypeEnum = IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET;
        for (PaymentSheetEntityExt item : paymentSheetList) {
            if (Objects.isNull(item.getId())) {
                continue;
            }
            //新增的数据，物理删除
            proEsHelper.physicalDeleteAllInAdd(item.getId(), corpid, indexTypeEnum);
        }
    }

    /**
     * 删除回款单
     * @param delEnum 删除标记
     * @param paymentSheetDeleteBatchDTO 删除入参dto
     * @param paymentSheetDeleteBatchVO 删除返回vo
     * @param delPaymentSheetList 要删除的回款单（此list只包含无父子的回款单，父回款单）
     * @param corpid 公司id
     * @param singleFlag 是否是单个删除
     * @throws XbbException
     * @author zcp
     * @date 19/12/6 006 23:05
     * @update 19/12/6 006 23:05
     * @since v1.0
     * @version v1.0
     */
    private void deletePaymentSheet(DelEnum delEnum, PaymentSheetDeleteBatchDTO paymentSheetDeleteBatchDTO, PaymentSheetDeleteBatchVO paymentSheetDeleteBatchVO, List<PaasFormDataEntityExt> delPaymentSheetList, String corpid, Integer singleFlag) throws XbbException {
        //删除的回款单编号list（用于记录日志，如果有删除失败的情况，则需要移除部分回款单编号）
        List<String> delNoList= new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        String attrSheetType = PaymentSheetEnum.getAttrConnectData(PaymentSheetEnum.PAYMENT_SHEET_TYPE);
        String attrOrigin = PaymentSheetEnum.getAttrConnectData(PaymentSheetEnum.RED_SHEET);
        List<String> redCodeList = PaymentSheetTypeEnum.getRedCodeList();
        //查询出关联的红冲回款单并塞入delPaymentSheetList内
        formHelp.getLinkRedSheetList(corpid, delPaymentSheetList, IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET, attrSheetType, attrOrigin, redCodeList);
        List<String> errorDataList = new ArrayList<>();
        Set<String> errorDataSet = new HashSet<>();
        Set<String> errorTypeSet = new HashSet<>();
        List<String> errorTypeList = new ArrayList<>();
        Set<Long> allCustomerIdSet = new HashSet<>();
        //客户id-该客户对应的预收款类型回款单编号（id）list：只有预收款类型删除是对客户余额减少，此时需要判断客户余额是否充足，不足则该客户下的预收款类型回款单不让删除
        Map<Long, List<String>> prepaymentNoMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, List<Long>> prepaymentIdMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        //父子单据相同的uuid - 该uuid对应的父单及其所有子单
        Map<String, List<Long>> uuidSheetMap = new HashMap<>(delPaymentSheetList.size());
        //合同id-该合同下的所有回款单id、编号集合（注意：不能单单用子回款单去构建关系map，因为父的回款单也要构建到关系内）
        Map<Long, List<Long>> cidSheetIdListMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, List<String>> cidSheetNoListMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        //应收款id-该应收款下的所有回款单id、编号集合（注意：不能单单用子回款单去构建关系map，因为父的回款单也要构建到关系内）
        Map<Long, List<Long>> pidSheetIdListMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, List<String>> pidSheetNoListMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        //资金账户ID-更改的金额
        Map<Long, Double> fundAccountNoListMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        //所有要删除的回款单id list（无父子的回款单，父、子回款单）
        List<Long> allDelIdList = new ArrayList<>();
        //更新关联合同、回款单计划的updateRelateList只包含了无父子回款单、子回款单，不包含父回款单。因为有父子回款单时更新关联业务，要依赖于明细（明细即子回款单，无明细的则依赖本身），因为父的会关联多个相同业务，如关联多个合同
        List<PaasFormDataEntityExt> updateRelateList = new ArrayList<>();
        Set<String> paymentSheetUuids = new HashSet<>();
        //回款单id-回款金额（预收款核销要读取核销金额）
        Map<Long, Double> sheetIdToAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        //2020/5/15 两个合同创建合并回款单 回款单开票,删除回款单 回款单被删除(原因:校验是否有发票的时候没有移除掉父回款单) 这构建父回款单uuid map
        Map<String,Long> parentPaymentSheetMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long,String> parentSerialNo = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (PaasFormDataEntityExt entityExt : delPaymentSheetList) {
            Integer flowStatus = entityExt.getFlowStatus();
            String serialNo = entityExt.getSerialNo();
            if ((Objects.equals(FlowStatusEnum.APPROVAL_PENDING.getType(), flowStatus) || Objects.equals(FlowStatusEnum.IN_APPROVAL.getType(), flowStatus)) && !Objects.equals(paymentSheetDeleteBatchDTO.getFromProcess(), 1)) {
                // 待审批和审批中数据不允许删除
                throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208025, PaymentErrorCodeEnum.API_ERROR_208025.getMsg(), serialNo);
            }
            JSONObject data = entityExt.getData();
            Long customerId = FastJsonHelper.getLongOrDefaultFromFormData(data, PaymentSheetEnum.CUSTOMER_ID.getAttr(), 0L);
            allCustomerIdSet.add(customerId);
            String paymentSheetType = FastJsonHelper.getStringFromFormData(data, PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr());
            Double amount = FundHelp.getSheetAmount4PaymentSheet(data, paymentSheetType);
            Long id = entityExt.getId();
            if (Objects.equals(paymentSheetType, PaymentSheetTypeEnum.PREPAYMENT.getCode())) {
                //预收款都没有明细
                List noList = prepaymentNoMap.getOrDefault(customerId, new ArrayList<>());
                noList.add(serialNo);
                List<Long> idList = prepaymentIdMap.getOrDefault(customerId, new ArrayList<>());
                idList.add(id);
                prepaymentNoMap.put(customerId, noList);
                prepaymentIdMap.put(customerId, idList);
            }
            sheetIdToAmount.put(id, amount);
            allDelIdList.add(id);
            delNoList.add(entityExt.getSerialNo());
            String uuid = entityExt.getUuid();
            if(Objects.equals(entityExt.getAlone(),SheetAloneEnum.NORMAL.getCode())){
                parentPaymentSheetMap.put(uuid,entityExt.getId());
                parentSerialNo.put(entityExt.getId(),entityExt.getSerialNo());
            }
            if (Objects.equals(entityExt.getAlone(), SheetAloneEnum.PARENT.getCode())) {
                //alone=父code，不需要更新关联合同、应收款。但其有子回款单：需通过该父单的uuid读取子回款单list
                paymentSheetUuids.add(uuid);
                parentPaymentSheetMap.put(uuid,entityExt.getId());
                parentSerialNo.put(entityExt.getId(),entityExt.getSerialNo());
                commonHelp.handleIdDataIdNoListMap(cidSheetIdListMap, cidSheetNoListMap, data, entityExt, PaymentSheetEnum.CONTRACT.getAttr());
                commonHelp.handleIdDataIdNoListMap(pidSheetIdListMap, pidSheetNoListMap, data, entityExt, PaymentSheetEnum.PAYMENT.getAttr());
                List<Long> collectList = uuidSheetMap.getOrDefault(uuid, new ArrayList<>());
                collectList.add(entityExt.getId());
                uuidSheetMap.put(uuid, collectList);
            } else {
                updateRelateList.add(entityExt);
            }
//            if (!Objects.equals(paymentSheetType, PaymentSheetTypeEnum.BAD_PAYMENT.getCode()) && !Objects.equals(paymentSheetType, PaymentSheetTypeEnum.WRITE_OFF_PREPAYMENT.getCode())) {
//                //资金账户余额处理
//                Long accountId = FastJsonHelper.getLongOrDefaultFromFormData(data, PaymentSheetEnum.FUND_ACCOUNT.getAttr(), 0L);
//                Double accountAmount = -amount;
//                fundAccountNoListMap.merge(accountId, accountAmount, Arith::add);
//            }
        }
        Set<Long> inApprovalIdInvoiceList = new HashSet<>();
        if (commonHelp.isOpenWorkFlow(corpid)) {
            BoolQueryBuilder boolQueryBuilder = boolQuery();
            boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
            boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
            boolQueryBuilder.filter(termsQuery(InvoiceEnum.getEsAttr4Keyword(InvoiceEnum.PAYMENT_SHEET_ID), allDelIdList));
            boolQueryBuilder.filter(termsQuery("flowStatus", Arrays.asList(FlowStatusEnum.APPROVAL_PENDING.getType(), FlowStatusEnum.IN_APPROVAL.getType())));
            boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_INVOICE.getType()));
            List<PaasFormDataEntityExt> invoiceFormDataList = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_INVOICE,
                    boolQueryBuilder, PaasFormDataEntityExt.class, Arrays.asList(InvoiceEnum.getAttrConnectData(InvoiceEnum.PAYMENT_SHEET_ID), FieldTypeEnum.DATAID.getAlias()));
            for (PaasFormDataEntityExt paasProcessDataEntity : invoiceFormDataList) {
                JSONArray paymentSheetArr = paasProcessDataEntity.getData().getJSONArray(InvoiceEnum.PAYMENT_SHEET_ID.getAttr());
                paymentSheetArr.forEach(item -> {
                    inApprovalIdInvoiceList.add(Long.valueOf(item.toString()));
                });
            }
        } else {
            List<PaasProcessDataEntity> processInvoiceDataEntityList = paasProcessDataModel.getInvoiceInApprovalDataByIdConditionJsonArray(XbbRefTypeEnum.INVOICE.getCode(), InvoiceEnum.PAYMENT_SHEET_ID.getAttr(), allDelIdList, corpid, Arrays.asList(FlowStatusEnum.APPROVAL_PENDING.getType(), FlowStatusEnum.IN_APPROVAL.getType()));
            for (PaasProcessDataEntity paasProcessDataEntity : processInvoiceDataEntityList) {
                JSONArray paymentSheetArr = JSONObject.parseObject(paasProcessDataEntity.getData()).getJSONArray(InvoiceEnum.PAYMENT_SHEET_ID.getAttr());
                paymentSheetArr.forEach(item -> {
                    inApprovalIdInvoiceList.add(Long.valueOf(item.toString()));
                });
            }
        }
        List<PaasFormDataEntityExt> removeList = new ArrayList<>();
        for (PaasFormDataEntityExt paasFormDataEntityExt : delPaymentSheetList) {
            if (inApprovalIdInvoiceList.contains(paasFormDataEntityExt.getId())) {
                if(!errorTypeList.contains(InvoiceErrorCodeEnum.API_ERROR_216035.getMsg())){
                    errorTypeList.add(InvoiceErrorCodeEnum.API_ERROR_216035.getMsg());
                }
                errorDataList.add(paasFormDataEntityExt.getSerialNo());
                removeList.add(paasFormDataEntityExt);
                if(CollectionsUtil.isNotEmpty(allDelIdList)) {
                    allDelIdList.remove(paasFormDataEntityExt.getId());
                }
                if(CollectionsUtil.isNotEmpty(delNoList)){
                    delNoList.remove(paasFormDataEntityExt.getSerialNo());
                }
            }
        }
        if(CollectionsUtil.isNotEmpty(removeList) && CollectionsUtil.isNotEmpty(delPaymentSheetList)){
            delPaymentSheetList.removeAll(removeList);
        }
        if (allDelIdList.size() > 0) {
            //退货退款的更新，不需要依赖子回款单
            refundService.updateRefund4Del(corpid, allDelIdList, sheetIdToAmount);
        }
        //要同步处理的子回款单list
        List<PaasFormDataEntityExt> childEntityExtList = fundHelp.getChildEsDataListByParentUuids(XbbRefTypeEnum.PAYMENT_SHEET, corpid, paymentSheetUuids, null, null);
        //构建子回款单map
        Map<Long,String> subPaymentSheetMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<String,List<Long>> uuidSubPaymentSheetMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<String,List<String>> subPaymentSheetSerailNo = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (PaasFormDataEntityExt child : childEntityExtList) {
            allDelIdList.add(child.getId());
            subPaymentSheetMap.put(child.getId(),child.getUuid());
            saasSaveHelp.addMapTwoId(uuidSubPaymentSheetMap,child.getUuid(),child.getId());
            saasSaveHelp.addMapTwoSerialNo(subPaymentSheetSerailNo,child.getUuid(),child.getSerialNo());
            String uuid = child.getUuid();
            List<Long> collectList = uuidSheetMap.getOrDefault(uuid, new ArrayList<>());
            collectList.add(child.getId());
            uuidSheetMap.put(uuid, collectList);
        }
        updateRelateList.addAll(childEntityExtList);
        //判断这些回款单（发票管理的子回款单）下面是否存在发票：该校验要在更新客户余额、合同/应收款的未收、已收、坏账等之前处理
        invoiceService.checkBusinessDeleteHaveInvoice(allDelIdList,corpid,XbbRefTypeEnum.PAYMENT_SHEET,errorTypeList,errorDataList,null,delNoList,parentPaymentSheetMap,parentSerialNo,subPaymentSheetMap,uuidSubPaymentSheetMap,subPaymentSheetSerailNo);
        //只包含了无父子回款单、子回款单，不包含父回款单
        int sizeUpdate = updateRelateList.size();
        SheetAttrPojo sheetAttrPojo = new SheetAttrPojo(XbbRefTypeEnum.PAYMENT_SHEET);
        SheetDeleteHandleRelateDTO sheetDeleteHandleRelateDTO = new SheetDeleteHandleRelateDTO(sizeUpdate, sheetAttrPojo, delNoList, uuidSheetMap, cidSheetIdListMap, cidSheetNoListMap, pidSheetIdListMap, pidSheetNoListMap);
        Iterator<PaasFormDataEntityExt> iterator = updateRelateList.iterator();
        while (iterator.hasNext()) {
            PaasFormDataEntityExt entityExt = iterator.next();
            if (allDelIdList.contains(entityExt.getId())) {
                commonHelp.setPaymentSheetDeleteHandleRelateDTO(sheetAttrPojo, entityExt, sheetDeleteHandleRelateDTO);
            } else {
                //未通过发票校验：回款单下有关联发票，不能删除回款单
                iterator.remove();
            }
        }
        List<Long> paymentIdIn = new ArrayList<>();
        List<Long> contractIdIn = new ArrayList<>();
        try {
            List<PaymentBalanceEntity> balanceList = new ArrayList<>();
            if (allCustomerIdSet.size() > 0) {
                balanceList = paymentBalanceService.updatePaymentBalance4Del(corpid, errorTypeSet, errorDataSet, allCustomerIdSet, prepaymentNoMap, prepaymentIdMap, allDelIdList, sheetDeleteHandleRelateDTO);
            }
            Set<Long> contractIds = sheetDeleteHandleRelateDTO.getContractIds();
            Set<Long> paymentIds = sheetDeleteHandleRelateDTO.getPaymentIds();
            List<UpdateDataEntity> updatePaymentList = new ArrayList<>();
            if (paymentIds.size() > 0) {
                //先处理是否能删除：校验删除后已收、未收、坏账值是否为负
                updatePaymentList = paymentService.updatePayment4Del(corpid, errorTypeSet, errorDataSet, allDelIdList, sheetDeleteHandleRelateDTO);
                paymentIdIn.addAll(paymentIds);
            }
            List<UpdateDataEntity> updateContractList = new ArrayList<>();
            if (contractIds.size() > 0) {
                //先处理是否能删除：校验删除后已收、未收、坏账值是否为负
                updateContractList = contractService.updateContract4Del(corpid, errorTypeSet, errorDataSet, allDelIdList, sheetDeleteHandleRelateDTO);
                contractIdIn.addAll(contractIds);
            }
            if (allDelIdList.size() > 0){
                if (BasicConstant.ONE.equals(singleFlag) && BasicConstant.ONE.equals(sizeUpdate)) {
                    paymentSheetModel.deleteByKeyAppointDel(allDelIdList.get(0), corpid, delEnum);
                } else {
                    paymentSheetModel.deleteBatch(allDelIdList, corpid, delEnum.getDel());
                }
                paymentSheetDeleteBatchVO.setDeleteIds(allDelIdList);
                //资金流水删除
                fundAccountFlowService.deleteBatch(allDelIdList, FundAccountFlowTypeEnum.PAYMENT_SHEET.getCode(), corpid);
                //对账单删除
                customerStatementService.deleteBatch(corpid, XbbRefTypeEnum.PAYMENT_SHEET.getCode(), allDelIdList);
            }
            try {
                if (updatePaymentList.size() > 0) {
                    paymentModel.updateBatch(updatePaymentList, corpid);
                }
                if (updateContractList.size() > 0) {
                    contractModel.updateBatch(updateContractList, corpid);
                }
                if (balanceList.size() > 0) {
                    paymentBalanceModel.updateBatchByCorpidAndCustomerId(balanceList, corpid);
                }
            } catch (Exception e) {
                LOG.error("PaymentSheetServiceImpl.deletePaymentSheet  关联更新客户/合同/应收款(balanceList、updatePaymentList、updateContractList)时报错", e);
                throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208048);
            }

            List<PaasFormDataEntityExt> allowDeletePaasFormDatas = new ArrayList<>(allDelIdList.size());

            for (PaasFormDataEntityExt entityExt : delPaymentSheetList) {
                if (!allDelIdList.contains(entityExt.getId())) {
                    continue;
                }
                JSONObject data = entityExt.getData();
                String paymentSheetType = FastJsonHelper.getStringFromFormData(data, PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr());
                if (SheetTypeEnum.effectFundAccount(paymentSheetType)) {
                    Double amount = FundHelp.getSheetAmount4PaymentSheet(data, paymentSheetType);
                    //资金账户余额处理
                    Long accountId = FastJsonHelper.getLongOrDefaultFromFormData(data, PaymentSheetEnum.FUND_ACCOUNT.getAttr(), 0L);
                    Double accountAmount = -amount;
                    fundAccountNoListMap.merge(accountId, accountAmount, Arith::add);
                }
                allowDeletePaasFormDatas.add(entityExt);
            }
            if (fundAccountNoListMap.size() > 0) {
                fundAccountService.saveBatchByPaymentSheetOrPaySheet(fundAccountNoListMap);
            }

            try {
                if (!Objects.equals(DistributorMarkEnum.DISTRIBUTOR.getCode(), paymentSheetDeleteBatchDTO.getDistributorMark())) {
                    PaymentSheetDynamicStrategy paymentSheetDynamicStrategy = dynamicStrategyFactory.getPaymentSheetDynamicStrategyByBusinessType(paymentSheetDeleteBatchDTO.getBusinessType());
                    FormDataDeleteBatchDTO formDataDeleteBatchDTO = BeanUtil.copyProperties(paymentSheetDeleteBatchDTO, FormDataDeleteBatchDTO.class, true);
                    paymentSheetDynamicStrategy.batchDelete(DynamicDeleteDTO.initDynamicDeleteDTO(formDataDeleteBatchDTO, allDelIdList, allowDeletePaasFormDatas));
                }
            } catch (Exception e) {
                LOG.error("com.xbongbong.saas.service.impl.PaymentSheetServiceImpl.deletePaymentSheet", e);
            }

            errorDataList.addAll(errorDataSet);
            errorTypeList.addAll(errorTypeSet);
            paymentSheetDeleteBatchVO.setErrorDataList(errorDataList);
            paymentSheetDeleteBatchVO.setErrorTypeList(errorTypeList);
            paymentSheetDeleteBatchVO.setErrorDataMemo(SaasDeleteHelp.getErrorDataMemo(errorTypeList, errorDataList));
            //生成删除日志
            delLog(paymentSheetDeleteBatchDTO, corpid, delNoList);
            //回款单删除
            if (delPaymentSheetList.size() > 0) {
                Long formId = delPaymentSheetList.get(0).getFormId();
                List<DataInfoDTO> dataInfoDTOs = new ArrayList<>();
                for (Long delId : allDelIdList) {
                    DataInfoDTO dataInfoDTO = new DataInfoDTO();
                    dataInfoDTO.setSaasMark(SaasMarkEnum.SAAS.getCode());
                    dataInfoDTO.setFormId(formId);
                    dataInfoDTO.setDataId(delId);
                    dataInfoDTOs.add(dataInfoDTO);
                }
            }
        } catch (XbbException e) {
            LOG.error("deletePaymentSheet rollback" + e);
            delRollBack4LinkEs(corpid, allDelIdList, paymentIdIn, contractIdIn, allCustomerIdSet);
            throw e;
        } catch (Exception e) {
            LOG.error("deletePaymentSheet rollback" + e);
            delRollBack4LinkEs(corpid, allDelIdList, paymentIdIn, contractIdIn, allCustomerIdSet);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
    }

    /**
     * 生成删除回款单日志
     * @param paymentSheetDeleteBatchDTO
     * @param corpid
     * @param delNoList
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void delLog(PaymentSheetDeleteBatchDTO paymentSheetDeleteBatchDTO, String corpid, List<String> delNoList) throws XbbException {
        int size = delNoList.size();
        if (size > BasicConstant.ZERO) {
            OperateTypeEnum operateTypeEnum = OperateTypeEnum.DELETE;
            if (Objects.equals(BasicConstant.ONE, paymentSheetDeleteBatchDTO.getIsBusinessRule())) {
                operateTypeEnum = OperateTypeEnum.RULE_DELETE;
            }
            //生成删除日志(区分单个删除和批量删除)
            String userId = paymentSheetDeleteBatchDTO.getUserId();
            String userName = paymentSheetDeleteBatchDTO.getLoginUserName();
            String nameStr = StringUtils.join(delNoList, "，");
            if(size > BasicConstant.ONE){
                String memo = format(I18nMessageUtil.getMessage(LogMemoConstant.LOG_MEMO_USER_BATCH_DELETE_FORM_DETAIL), userName, XbbRefTypeEnum.PAYMENT_SHEET.getName(), size);
                List<InfoArrPojo> infoArrPojoList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                InfoArrPojo infoArrPojo = new InfoArrPojo();
                infoArrPojo.setTitle(PaymentSheetEnum.SHEET_NO.getAttrName());
                infoArrPojo.setContent(nameStr);
                infoArrPojoList.add(infoArrPojo);
                mongoLogHelp.buildLog4InfoArr(infoArrPojoList, corpid, userId, userName, OperateModuleTypeEnum.PaymentManage, operateTypeEnum,
                        "", "", memo, paymentSheetDeleteBatchDTO.getHttpHeader());
            } else if (Objects.equals(size, BasicConstant.ONE)){
                String memo = format(I18nMessageUtil.getMessage(LogMemoConstant.LOG_MEMO_USER_DELETE_WORK), userName, XbbRefTypeEnum.PAYMENT_SHEET.getName(), nameStr);
                mongoLogHelp.buildLog(corpid, userId, userName, OperateModuleTypeEnum.PaymentManage, operateTypeEnum,
                        delNoList.toString(), nameStr, memo, paymentSheetDeleteBatchDTO.getHttpHeader());
            }
        }
    }

    @Override
    public void formatAddExplain(FormatExplainDTO formatExplainDTO) throws XbbException {
        List<FieldAttrEntity> explainList = formatExplainDTO.getExplainList();
        boolean distributorMarkFlag = Objects.equals(formatExplainDTO.getDistributorMark(), DistributorMarkEnum.DISTRIBUTOR.getCode());
        for (FieldAttrEntity fieldAttrEntity : explainList) {
            String attr = fieldAttrEntity.getAttr();
            //处理类型、经销商回款单支付方式的showType
//            boolean showTypeListDetail = distributorMarkFlag && Objects.equals(attr, PaymentSheetBaseEnum.PAY_METHOD.getAttr());
            if (Objects.equals(attr, PaymentSheetEnum.PAYMENT_TIME.getAttr())) {
                DefaultAttrPoJo defaultAttrPoJo = fieldAttrEntity.getDefaultAttr();
                if (StringUtil.isEmpty(defaultAttrPoJo.getDefaultType())) {
                    defaultAttrPoJo.setDefaultType(DefaultTypeEnum.DATE_BY_BACK_END.getAlias());
                }
                //回款日期字段：非自定义类型才设置默认值
                if (!Objects.equals(defaultAttrPoJo.getDefaultType(),DefaultTypeEnum.CUSTOM.getAlias())){
                    defaultAttrPoJo.setDefaultValue(DateTimeUtil.getInt());
                }
            }
        }
    }

    @Override
    public void formatLinkAddRedPrepayment(HandlerExplainInLinkItemDTO handlerExplainInLinkItemDTO) throws XbbException {
        JSONObject linkFormData = handlerExplainInLinkItemDTO.getLinkFormData();
        Long linkDataId = handlerExplainInLinkItemDTO.getLinkDataId();
        List<FieldAttrEntity> explainList = handlerExplainInLinkItemDTO.getExplainList();
        FieldAttrEntity customerField = new FieldAttrEntity();
        FieldAttrEntity timeField = new FieldAttrEntity();
        FieldAttrEntity fundAccountField = new FieldAttrEntity();
        for(FieldAttrEntity fieldAttrEntity : explainList) {
            if (Objects.equals(fieldAttrEntity.getAttr(), PaymentSheetEnum.CUSTOMER_ID.getAttr())) {
                customerField = fieldAttrEntity;
            } else if (Objects.equals(fieldAttrEntity.getAttr(), PaymentSheetEnum.PAYMENT_TIME.getAttr())) {
                timeField = fieldAttrEntity;
            } else if (Objects.equals(fieldAttrEntity.getAttr(), PaymentSheetEnum.FUND_ACCOUNT.getAttr())) {
                fundAccountField = fieldAttrEntity;
            }
        }
        PaymentBalanceEntity paymentBalanceEntity = paymentBalanceModel.getByCustomerId(linkDataId, handlerExplainInLinkItemDTO.getCorpid());
        // 关联客户
        customerField.setDefaultAttr(saasUpdateHelp.getDefaultAttrPojoByPaymentBalance(linkDataId, linkFormData.getString(CustomerManagementEnum.NAME.getAttr()), paymentBalanceEntity));
        // 红冲日期
        DefaultAttrPoJo timeDefaultAttrPoJo = timeField.getDefaultAttr();
        if (StringUtil.isEmpty(timeDefaultAttrPoJo.getDefaultType())) {
            timeDefaultAttrPoJo.setDefaultType(DefaultTypeEnum.DATE_BY_BACK_END.getAlias());
        }
        timeDefaultAttrPoJo.setDefaultValue(DateTimeUtil.getInt());
        timeField.setDefaultAttr(timeDefaultAttrPoJo);
        //红冲资金账户
        String fundAccountValue = FastJsonHelper.getStringFromFormData(linkFormData, PaymentSheetEnum.FUND_ACCOUNT.getAttr());
        commonHelp.handleLinkAdd4Combo(fundAccountField, fundAccountValue);
    }

    /**
     * 获取回款单
     *
     * @param paymentSheetSaveDTO 入参
     * @return 回参
     * @throws XbbException 异常
     */
    @Override
    public PaymentSheetGetVO get(PaymentSheetGetDTO paymentSheetSaveDTO) throws XbbException {
        PaymentSheetGetVO paymentSheetGetVO = new PaymentSheetGetVO();
        Integer businessType = paymentSheetSaveDTO.getBusinessType();
        Long refId = paymentSheetSaveDTO.getRefId();
        Map<String, Object> dataParam = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        XbbRefTypeEnum xbbRefTypeEnum = XbbRefTypeEnum.getByCode(businessType);
        switch (xbbRefTypeEnum) {
            case PAYMENT:
                dataParam.put(PaymentSheetEnum.PAYMENT.getAttr(), refId);
                break;
            case CONTRACT:
                //TODO 资金管理改造-该接口似乎未启用，暂不处理合同字段
                dataParam.put(PaymentSheetEnum.CONTRACT.getAttr(), refId);
                break;
            default:
                throw new XbbException(TemplateErrorCodeEnum.API_ERROR_350006);
        }
        dataParam.put(PaymentSheetEnum.IS_BAD.getAttr(), BasicConstant.ZERO);
        dataParam.put(PaymentSheetEnum.IS_RED.getAttr(), BasicConstant.ZERO);
        dataParam.put(PaymentSheetEnum.RED_SHEET.getAttr(), BasicConstant.ZERO);
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put(ParameterConstant.CORPID, paymentSheetSaveDTO.getCorpid());
        param.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
        param.put(ParameterConstant.FLOW_STATUS_IN, Arrays.asList(FlowStatusEnum.NOT_NEED_APPROVAL.getType(), FlowStatusEnum.PASS.getType()));
        List<PaasFormDataEntityExt> paasFormDataEntityExts = paasEsModel.selectByCondition(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), dataParam, param, IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET);
        JSONArray paymentSheetArr = new JSONArray();
        for (PaasFormDataEntityExt sheet : paasFormDataEntityExts) {
            JSONObject dataList = sheet.getData();
            JSONObject obj = new JSONObject();
            obj.put(StringConstant.SAAS_LINK_BUSINESS_ID, sheet.getDataId());
            obj.put("sheetNo", sheet.getSerialNo());
            Long paymentTime = FastJsonHelper.getLongOrDefaultFromFormData(dataList, PaymentSheetEnum.PAYMENT_TIME.getAttr(), 0L);
            String timeStr = DateTimeUtil.getString(paymentTime, DateTimeUtil.SDFDate);
            obj.put("paymentTime", timeStr);
            Double amount = getDoubleOrDefaultFromFormData(dataList, PaymentSheetEnum.AMOUNT.getAttr(), 0D);
            obj.put("amount", amount);
            paymentSheetArr.add(obj);
        }
        paymentSheetGetVO.setPaymentSheetArr(paymentSheetArr);
        return paymentSheetGetVO;
    }

    /**
     * 保存, 暂时没有引用，引用的话需要修改内部的日志为动态
     *
     * @param paymentSheetSaveDTO 入参
     * @return 返回数据id
     * @throws XbbException 异常
     */
    @Override
    public FormDataAddVO add(PaymentSheetSaveDTO paymentSheetSaveDTO, PaasFormDataEntity paasFormDataEntity, PaasFormEntityExt paasFormEntityExt) throws XbbException {
        try {
            FormDataAddVO formDataAddVO = new FormDataAddVO();
            JSONObject dataList = paasFormDataEntity.getData();
            Integer businessType = paymentSheetSaveDTO.getBusinessType();
            SheetTypeEnum sheetTypeEnum = SheetTypeEnum.getByBusinessType(businessType);
            Long refundId = dataList.getLong(PaasConstant.REFUND_ID);
            //删除不在mapping中的字段  兼容老数据
            fundHelp.removeNotInMapping(dataList);
            fundHelp.removeNotInMapping(paymentSheetSaveDTO.getDataList());
            //明细
            fundHelp.removeNotInMapping4Array(paymentSheetSaveDTO.getAmountDetail());
            //校验回款单金额不能为0
            saasSaveHelper.checkSheetAmount(sheetTypeEnum, dataList);
            //提示:有合同 关联的回款单才会生成 客户对账单
            Long modelType = fundSetService.getModelType(paymentSheetSaveDTO.getCorpid(), XbbRefTypeEnum.CRM);
            paasFormDataEntity.setSysLong10(modelType);
            if (Objects.equals(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), businessType)) {
                //已核销
                writeOffSave(PaymentSheetTypeEnum.WRITE_OFF, paymentSheetSaveDTO, paasFormDataEntity, paasFormEntityExt);
            } else if (Objects.equals(XbbRefTypeEnum.WRITE_OFF_PREPAYMENT.getCode(), businessType)) {
                //预收款核销
                writeOffSave(PaymentSheetTypeEnum.WRITE_OFF_PREPAYMENT, paymentSheetSaveDTO, paasFormDataEntity, paasFormEntityExt);
            } else if (Objects.equals(XbbRefTypeEnum.PREPAYMENT.getCode(), businessType)) {
                //预收款
                prepaymentSave(SheetTypeEnum.PREPAYMENT, paymentSheetSaveDTO, paasFormDataEntity, paasFormEntityExt);
            } else if (Objects.equals(XbbRefTypeEnum.RED_PAYMENT_SHEET.getCode(), businessType)) {
                //红冲已核销
                redSave(SheetTypeEnum.RED_WRITE_OFF, paymentSheetSaveDTO, paasFormDataEntity, paasFormEntityExt);
            } else if (Objects.equals(XbbRefTypeEnum.RED_WRITE_OFF_PREPAYMENT.getCode(), businessType)) {
                //红冲预收款核销
                redSave(SheetTypeEnum.RED_WRITE_OFF_PREPAYMENT, paymentSheetSaveDTO, paasFormDataEntity, paasFormEntityExt);
            } else if (Objects.equals(XbbRefTypeEnum.RED_WRITE_OFF_BALANCE.getCode(), businessType)) {
                //红冲预收款核销(退到余额)
                redSave(SheetTypeEnum.RED_WRITE_OFF_BALANCE, paymentSheetSaveDTO, paasFormDataEntity, paasFormEntityExt);
            } else if (Objects.equals(XbbRefTypeEnum.RED_PREPAYMENT.getCode(), businessType)) {
                //红冲预收款余额
                prepaymentSave(SheetTypeEnum.RED_PREPAYMENT, paymentSheetSaveDTO, paasFormDataEntity, paasFormEntityExt);
            } else if (Objects.equals(XbbRefTypeEnum.BAD_DEBT.getCode(), businessType)) {
                //坏账
                badSave(paymentSheetSaveDTO, paasFormDataEntity, paasFormEntityExt);
            }
            if (refundId != null && Objects.equals(paasFormEntityExt.getIsProcessForm(), BasicConstant.ONE)) {
                saasRefundHelper.updateRefundPaymentSheetId(refundId, paasFormDataEntity, paymentSheetSaveDTO.getCorpid());
            }
            formDataAddVO.setMsg(I18nMessageUtil.getMessage(MessageConstant.FORM_ADD_RETURN_MSG));
            formDataAddVO.setCode(PaasConstant.FORM_ADD_RETURN_CODE);
            return formDataAddVO;
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("com.xbongbong.saas.service.impl.PaymentSheetServiceImpl.add error", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
    }

    @Deprecated
    @Override
    public void writeOffSave(PaymentSheetTypeEnum paymentSheetTypeEnum, PaymentSheetSaveDTO paymentSheetSaveDTO, PaasFormDataEntity paasFormDataEntity, PaasFormEntityExt paasFormEntityExt) throws XbbException{
        /*
         已核销回款：回款金额、核销金额同时置值
         */
        JSONObject dataList = paymentSheetSaveDTO.getDataList();
        String corpid = paymentSheetSaveDTO.getCorpid();
        String userId = paymentSheetSaveDTO.getUserId();
        if (Objects.nonNull(paymentSheetSaveDTO.getProcessCreatorId())) {
            userId = paymentSheetSaveDTO.getProcessCreatorId();
        }
        JSONArray amountDetails = paymentSheetSaveDTO.getAmountDetail();
        //TODO 金额校验，后续修改成根据每条明细判断，暂时未用到
        double allAmount = 0D;
        //应收款和合同对应的回款金额
        Map<Long, Double> contractIdToAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, Double> paymentIdToAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Set<Long> contractIds = new HashSet<>();
        Set<Long> paymentIds = new HashSet<>();
        //回款单的实收回款金额
        Double sheetAmount;
        boolean writeOffPrepaymentFlag = SheetTypeEnum.onlyWriteOffAmount(paymentSheetTypeEnum.getCode());
        if (writeOffPrepaymentFlag) {
            //预收款核销，是用的核销金额
            sheetAmount = getDoubleOrDefaultFromFormData(dataList, PaymentSheetEnum.WRITE_OFF_AMOUNT.getAttr(), 0D);
            //检查预收款核销金额是否小于客户余额
            checkCustomerBalance(dataList, corpid, sheetAmount);
        } else {
            sheetAmount = getDoubleOrDefaultFromFormData(dataList, PaymentSheetEnum.AMOUNT.getAttr(), 0D);
        }
        SheetTypeEnum sheetTypeEnum = paymentSheetTypeEnum.getSheetTypeEnum();
        SheetAttrPojo sheetAttrPojo = new SheetAttrPojo(XbbRefTypeEnum.PAYMENT_SHEET);
        /*
          经销商类型回款单，需要做如下事情：
          （1）（经销商端判断，此处不判断）上传凭证：线下支付则必填
          （2）支付方式，厂商端操作需后端自动赋值 - handleAmountDetail方法内处理
                代客操作：已核销，预收款
                预收款账户：预收款核销，红冲预收款核销（退到余额）
                线下打款：红冲预收款核销（原Pro的），红冲预收款余额
                另外，红冲已核销与原单一致（关联新建时处理好）
          （3）上游订货单（合同）付款状态更新
         */
        Integer distributorMark = paasFormEntityExt.getDistributorMark();
        paymentSheetSaveDTO.setDistributorMark(distributorMark);
        //释放需要回滚信用流水
        boolean creditRollBack = false;
        //处理金额明细
        List<JSONObject> sonPaymentSheetDataList = saasSaveHelp.handleAmountDetail(paymentSheetSaveDTO, sheetAmount, sheetTypeEnum, sheetAttrPojo, dataList, amountDetails, allAmount, contractIdToAmount, paymentIdToAmount, contractIds, paymentIds);
        Double sumAmount = getSumAmount(paymentSheetSaveDTO, contractIds, paymentIds, null);
        //允许误差小于等于ConfigConstant.amountAccuracy
        if (Arith.sub(sheetAmount , sumAmount) > ConfigConstant.amountAccuracy) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.BEYOND_AMOUNT);
        }
        //处理应收款并封装要更新的结果：判断已收金额是否已经超过应收款内的未收金额，并把应收款的已收金额增加及更改其回款状态等
        HandleLinkInfoVO paymentLinkInfoVO = paymentService.handlePayment(paymentSheetTypeEnum, dataList, corpid, paymentIdToAmount, paymentIds);
        List<UpdateDataEntity> updatePayList = paymentLinkInfoVO.getUpdateList();
        //处理合同并封装要更新的结果：判断已收金额是否已经超过合同内的未收金额，并把合同的已收金额增加等
        List<UpdateDataEntity> updateConList = contractService.handleContract(distributorMark, paymentSheetTypeEnum, corpid, contractIdToAmount, contractIds);
        PaymentSheetEntityExt paymentSheetEntityExt = new PaymentSheetEntityExt();
        BeanUtil.copyProperties(paasFormDataEntity, paymentSheetEntityExt);
        fundHelp.initSheetEntity4Save(userId, paymentSheetEntityExt);
        for (Map.Entry<String,Object> entry : SaasDataInitHelp.initPaymentSheet().entrySet()){
            SaasDataInitHelp.antDataSet(dataList,entry.getKey(),entry.getValue());
        }
        long now = DateUtil.getNow();
        paymentSheetEntityExt.setData(dataList);
        String uuid = UUID.randomUUID().toString();
        paymentSheetEntityExt.setUuid(uuid);
        paymentSheetEntityExt.setAddTime(now);
        paymentSheetEntityExt.setUpdateTime(now);
        //插入父回款、子回款
        if (sonPaymentSheetDataList.size() > BasicConstant.ONE) {
            paymentSheetEntityExt.setAlone(SheetAloneEnum.PARENT.getCode());
        } else {
            //没有明细，父子合并
            paymentSheetEntityExt.setAlone(SheetAloneEnum.NORMAL.getCode());
        }
        List<PaymentSheetEntityExt> sonSheetList = null;
        Long sheetId = null;
        //预收款核销、红冲预收款核销(退到余额)不入流水，但普通核销入流水
        boolean flowFlag = !writeOffPrepaymentFlag;
        List<Long> taskIdIn = null;
        try {
            paymentSheetModel.save(paymentSheetEntityExt);
            sheetId = paymentSheetEntityExt.getId();
            //回滚ES 需要dataID
            paasFormDataEntity.setId(sheetId);
            sonSheetList = saasPaymentSheetHelper.savePaymentSheetSon(corpid, sonPaymentSheetDataList, paymentSheetEntityExt, uuid, paymentLinkInfoVO);
            //更新应收款/合同
            paymentModel.savePaymentPlan(corpid, updatePayList);
            contractHelp.saveContract(corpid, updateConList, "PaymentSheetServiceImpl.normalSave：新建已核销回款单更新合同时失败");
            Map<String, FieldAttrEntity> explainMap = paymentSheetSaveDTO.getExplainMap();
            if (writeOffPrepaymentFlag) {
                //核销预收款回款单新建时，减少预收款余额
                saasPaymentSheetHelper.saveByPaymentSheet(paymentSheetEntityExt, distributorMark);
            } else {
                Long addTime = paymentSheetEntityExt.getAddTime();
                Long accountId = FastJsonHelper.getLongOrDefaultFromFormData(dataList, PaymentSheetEnum.FUND_ACCOUNT.getAttr(), 0L);
                Long sheetTime = fundHelp.getSheetTime4FundFlow(dataList, PaymentSheetEnum.PAYMENT_TIME.getAttr(), explainMap, addTime);
                //账户余额变动
                payAndPaymentSheetHelp.saveByPaymentSheetOrPaySheet(accountId, sheetAmount, corpid);
                //流水表日志记录
                FundAccountFlowEntity fundAccountFlowEntity = new FundAccountFlowEntity(corpid, addTime, paymentSheetEntityExt.getCreatorId(), FundAccountFlowTypeEnum.PAYMENT_SHEET.getCode(), sheetId, accountId, sheetTime, sheetAmount);
                fundAccountFlowModel.insert(fundAccountFlowEntity);
            }
            //按最小纬度进项生成客户对账单
            if (sonSheetList.size() > BasicConstant.ONE) {
                saasCustomerStatementHelper.save(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), explainMap, sonSheetList, distributorMark);
            } else {
                saasCustomerStatementHelper.save(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), explainMap, Arrays.asList(paymentSheetEntityExt), distributorMark);
            }
            //处理回款单团队-必须在团队保存后执行，需要用到回款单的id
            saasPaymentSheetHelper.handleSheetTeam(dataList, corpid, sheetTypeEnum, paymentSheetEntityExt, sonSheetList, true);
            if (Objects.equals(distributorMark, DistributorMarkEnum.DISTRIBUTOR.getCode())) {
                Long distributorId = dataList.getLong(PaymentSheetDistributorEnum.DISTRIBUTOR_ID.getAttr());
                String distributorName = dataList.getString(PaymentSheetDistributorEnum.DISTRIBUTOR_NAME_LINK_TEXT.getAttr());
                if (!contractIds.isEmpty()) {
                    if (Objects.equals(paymentSheetTypeEnum, PaymentSheetTypeEnum.WRITE_OFF)) {
                        // 回款单直接生成流水 > 该订货单有信用流水时，回款单用来还信用，即释放信用
                        List<Long> creditOrderIds = saasCreditHelper.filterCreditOrderIds(new ArrayList<>(contractIds), corpid);
                        if (creditOrderIds.size() > 0) {
                            creditRollBack = true;
                            Double creditOrderAmount = 0D;
                            for (Long creditOrderId : creditOrderIds) {
                                creditOrderAmount = Arith.add(creditOrderAmount, contractIdToAmount.getOrDefault(creditOrderId, 0D));
                            }
                            CreditCardBillInfoPojo infoPojo = new CreditCardBillInfoPojo(CreditCardBillTypeEnum.PAYMENT_SHEET.getCode(), sheetId, paymentSheetEntityExt.getSerialNo(), creditOrderAmount, distributorId, distributorName, XbbRefTypeEnum.PAYMENT_SHEET.getCode());
                            CreditCardBillDTO creditCardBillDTO = new CreditCardBillDTO(Arrays.asList(infoPojo));
                            BeanUtil.copyProperties(paymentSheetSaveDTO, creditCardBillDTO);
                            saasCreditHelper.addBatchCreditCardBill(creditCardBillDTO);
                        }
                    } else if (Objects.equals(paymentSheetTypeEnum, PaymentSheetTypeEnum.WRITE_OFF_PREPAYMENT)) {
                        //预收款核销生成信用流水
                        BigDecimal bigDecimal = saasCreditHelper.calcCreditPayLimit(distributorId, new ArrayList<>(contractIds), new ArrayList<>(paymentIds), corpid);
                        if (bigDecimal.compareTo(BigDecimal.ZERO) > 0) {
                            creditRollBack = true;
                            CreditCardBillInfoPojo infoPojo = new CreditCardBillInfoPojo(CreditCardBillTypeEnum.PAYMENT_SHEET.getCode(), sheetId, paymentSheetEntityExt.getSerialNo(), bigDecimal.doubleValue(), distributorId, distributorName, XbbRefTypeEnum.PAYMENT_SHEET.getCode());
                            CreditCardBillDTO creditCardBillDTO = new CreditCardBillDTO(Arrays.asList(infoPojo));
                            BeanUtil.copyProperties(paymentSheetSaveDTO, creditCardBillDTO);
                            DistributorBaseInfoEntity distributorBaseInfoEntity = paymentSheetSaveDTO.getDistributorBaseInfoEntity();
                            if (Objects.nonNull(distributorBaseInfoEntity)) {
                                creditCardBillDTO.setUserId(distributorBaseInfoEntity.getDistributorUserId().toString());
                                creditCardBillDTO.setLoginUserName(distributorBaseInfoEntity.getDistributorUserName());
                                infoPojo.setCreatorIsDistributor(BasicConstant.ONE);
                            }
                            saasCreditHelper.addBatchCreditCardBill(creditCardBillDTO);
                        }
                    }
                }
            }
            // 添加项目日志，及推送消息
            userId = saasSaveHelp.sheetAddLog(paymentSheetSaveDTO, corpid, userId, paymentSheetEntityExt, XbbRefTypeEnum.PAYMENT_SHEET.getName(), OperateModuleTypeEnum.PaymentManage);
            createLogAndMessage(PushTypeEnum.PAYMENT_SHEET_ADD, dataList, corpid, userId, paymentSheetEntityExt, I18nMessageUtil.getMessage(CustomerCommunicateConstant.PAYMENT_SHEET_ADD_CONTRACT), I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_ADD_TITLE), I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_ADD_CONTENT), distributorMark);
        } catch (XbbException e) {
            //回滚子单，流水(无流水的不需回滚)，对账记录，应收款，合同，预收款余额(未处理余额则无需回滚)，回款计划
            Long customerId = dataList.getLongValue(PaymentSheetEnum.CUSTOMER_ID.getAttr());
            addRollBack4LinkEs(corpid, sheetId, sonSheetList, flowFlag, paymentIds, contractIds, writeOffPrepaymentFlag, Collections.singletonList(customerId), taskIdIn, creditRollBack);
            throw e;
        } catch (Exception e) {
            //回滚子单，流水(无流水的不需回滚)，对账记录，应收款，合同，预收款余额(未处理余额则无需回滚)，回款计划
            Long customerId = dataList.getLongValue(PaymentSheetEnum.CUSTOMER_ID.getAttr());
            addRollBack4LinkEs(corpid, sheetId, sonSheetList, flowFlag, paymentIds, contractIds, writeOffPrepaymentFlag, Collections.singletonList(customerId), taskIdIn, creditRollBack);
            LOG.error("保存回款单到数据库报错", e);
            throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208052, PaymentErrorCodeEnum.API_ERROR_208052.getMsg(), XbbRefTypeEnum.PAYMENT_SHEET.getName());
        }
    }

    @Override
    public void prepaymentSave(SheetTypeEnum sheetTypeEnum, PaymentSheetSaveDTO paymentSheetSaveDTO, PaasFormDataEntity paasFormDataEntity, PaasFormEntityExt paasFormEntityExt) throws XbbException{
        /*
         预收款只关联了客户，其红冲也是如此，因此除了客户余额无需更新合同、应收款
         预收款：只存回款金额，核销金额为空
         红冲预收款余额：只存回款金额、且为负，核销金额为空（需要把金额置为负数，并校验客户余额是否足够红冲）
         */
        JSONObject dataList = paymentSheetSaveDTO.getDataList();
        String corpid = paymentSheetSaveDTO.getCorpid();
        String userId = paymentSheetSaveDTO.getUserId();
        if (Objects.nonNull(paymentSheetSaveDTO.getProcessCreatorId())) {
            userId = paymentSheetSaveDTO.getProcessCreatorId();
        }
        boolean redFlag = false;
        if (Objects.equals(sheetTypeEnum, SheetTypeEnum.RED_PREPAYMENT)) {
            redFlag = true;
            Double amount = dataList.getDouble(PaymentSheetEnum.AMOUNT.getAttr());
            //检查红冲预收款余额的红冲金额是否小于客户余额
            checkCustomerBalance(dataList, corpid, amount);
            //金额置为负数
            dataList.put(PaymentSheetEnum.AMOUNT.getAttr(), Arith.mul(amount, -1));
        }
        PaymentSheetEntityExt paymentSheetEntityExt = new PaymentSheetEntityExt();
        BeanUtil.copyProperties(paasFormDataEntity, paymentSheetEntityExt);
        fundHelp.initSheetEntity4Save(userId, paymentSheetEntityExt);
        for (Map.Entry<String,Object> entry : SaasDataInitHelp.initPaymentSheet().entrySet()){
            SaasDataInitHelp.antDataSet(dataList,entry.getKey(),entry.getValue());
        }
        long now = DateUtil.getNow();
        paymentSheetEntityExt.setData(dataList);
        String uuid = UUID.randomUUID().toString();
        paymentSheetEntityExt.setUuid(uuid);
        paymentSheetEntityExt.setAlone(SheetAloneEnum.NORMAL.getCode());
        paymentSheetEntityExt.setAddTime(now);
        paymentSheetEntityExt.setUpdateTime(now);
        Long sheetId = null;
        Long customerId = dataList.getLong(PaymentSheetEnum.CUSTOMER_ID.getAttr());
        try {
            paymentSheetModel.save(paymentSheetEntityExt);
            sheetId = paymentSheetEntityExt.getId();
            paasFormDataEntity.setId(sheetId);
            //添加预收款余额
            Integer distributorMark = paasFormEntityExt.getDistributorMark();
            saasPaymentSheetHelper.saveByPaymentSheet(paymentSheetEntityExt, distributorMark);

            Map<String, FieldAttrEntity> explainMap = paymentSheetSaveDTO.getExplainMap();
            Long accountId = FastJsonHelper.getLongOrDefaultFromFormData(dataList, PaymentSheetEnum.FUND_ACCOUNT.getAttr(), 0L);
            Long addTime = paymentSheetEntityExt.getAddTime();
            Long sheetTime = fundHelp.getSheetTime4FundFlow(dataList, PaymentSheetEnum.PAYMENT_TIME.getAttr(), explainMap, addTime);
            Double amount = FastJsonHelper.getDoubleOrDefaultFromFormData(dataList, PaymentSheetEnum.AMOUNT.getAttr(), 0D);
            //账户余额变动
            payAndPaymentSheetHelp.saveByPaymentSheetOrPaySheet(accountId, amount, corpid);
            //流水表日志记录
            FundAccountFlowEntity fundAccountFlowEntity = new FundAccountFlowEntity(corpid, addTime, paymentSheetEntityExt.getCreatorId(), FundAccountFlowTypeEnum.PAYMENT_SHEET.getCode(), paymentSheetEntityExt.getId(), accountId, sheetTime, amount);
            fundAccountFlowModel.insert(fundAccountFlowEntity);
            //处理回款单团队-必须在团队保存后执行，需要用到回款单的id
            Map<Long, List<Long>> sheetIdAndDataIdMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            List<Long> contractIdIn = new ArrayList<>();
            fundHelp.handleSheetIdAndCusIdMap(contractIdIn, sheetIdAndDataIdMap, paymentSheetEntityExt, PaymentSheetEnum.CUSTOMER_ID.getAttr());
            userTeamService.createPaymentSheetUser(corpid, customerId, contractIdIn, sheetIdAndDataIdMap, sheetTypeEnum, false);
            //按最小纬度进项生成客户对账单
            List<PaasFormDataEntityExt> paymentSheetInsertList = new ArrayList<>();
            paymentSheetInsertList.add(paymentSheetEntityExt);
            //记录客户对账单
            saasCustomerStatementHelper.save(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), explainMap, paymentSheetInsertList, distributorMark);
            // 添加项目日志，及推送消息
            userId = saasSaveHelp.sheetAddLog(paymentSheetSaveDTO, corpid, userId, paymentSheetEntityExt, XbbRefTypeEnum.PAYMENT_SHEET.getName(), OperateModuleTypeEnum.PaymentManage);
            PushTypeEnum pushTypeEnum;
            String logMemo;
            String pushTitle;
            String pushContent;
            XbbRefTypeEnum xbbRefTypeEnum;
            if (redFlag) {
                pushTypeEnum = PushTypeEnum.PAYMENT_SHEET_RED_PREPAYMENT_ADD;
                logMemo = I18nMessageUtil.getMessage(CustomerCommunicateConstant.PAYMENT_SHEET_RED_PREPAYMENT_ADD);
                pushTitle = I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_RED_PREPAYMENT_ADD_TITLE);
                pushContent = I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_RED_PREPAYMENT_ADD_CONTENT);
                xbbRefTypeEnum = XbbRefTypeEnum.RED_PREPAYMENT;
            } else {
                pushTypeEnum = PushTypeEnum.PAYMENT_SHEET_PREPAYMENT_ADD;
                logMemo = I18nMessageUtil.getMessage(CustomerCommunicateConstant.PAYMENT_SHEET_PREPAYMENT_ADD);
                pushTitle = I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_PREPAYMENT_ADD_TITLE);
                pushContent = I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_PREPAYMENT_ADD_CONTENT);
                xbbRefTypeEnum = XbbRefTypeEnum.PREPAYMENT;
            }
            createLogAndMessage4Pre(xbbRefTypeEnum, pushTypeEnum, dataList, corpid, userId, paymentSheetEntityExt, logMemo, pushTitle, pushContent, distributorMark);
        } catch (XbbException e) {
            //只回滚资金流水，对账单记录，预收款余额
            addRollBack4LinkEs(corpid, sheetId, null, true, null, null, true, Collections.singletonList(customerId), null, false);
            throw e;
        } catch (Exception e) {
            //只回滚资金流水，对账单记录，预收款余额
            addRollBack4LinkEs(corpid, sheetId, null, true, null, null, true, Collections.singletonList(customerId), null, false);
            LOG.error("保存回款单到数据库报错", e);
            throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208052, PaymentErrorCodeEnum.API_ERROR_208052.getMsg(), XbbRefTypeEnum.PAYMENT_SHEET.getName());
        }
    }

    @Deprecated
    @Override
    public void redSave(SheetTypeEnum sheetTypeEnum, PaymentSheetSaveDTO paymentSheetSaveDTO, PaasFormDataEntity paasFormDataEntity, PaasFormEntityExt paasFormEntityExt) throws XbbException{
        /*
         红冲已核销、红冲预收款核销：回款金额，核销金额都为负，且一致
         红冲预收款核销(退到余额)：只有核销金额，且为负数
         其中红冲预收款核销(退到余额)影响预收款余额，不影响资金账户
         */
        JSONObject dataList = paymentSheetSaveDTO.getDataList();
        String corpid = paymentSheetSaveDTO.getCorpid();
        String userId = paymentSheetSaveDTO.getUserId();
        if (Objects.nonNull(paymentSheetSaveDTO.getProcessCreatorId())) {
            userId = paymentSheetSaveDTO.getProcessCreatorId();
        }
        JSONArray amountDetails = paymentSheetSaveDTO.getAmountDetail();
        //TODO 金额校验，后续修改成根据每条明细判断，暂时未用到
        double allAmount = 0D;
        //应收款和合同对应的回款金额
        Map<Long, Double> contractIdToAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, Double> paymentIdToAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Set<Long> contractIds = new HashSet<>();
        Set<Long> paymentIds = new HashSet<>();
        //回款单的红冲回款金额（此时还未处理成负数）
        Double sheetAmount;
        boolean redWriteOffBalanceFlag = SheetTypeEnum.onlyWriteOffAmount(sheetTypeEnum.getCode());
        if (redWriteOffBalanceFlag) {
            //红冲预收款核销(退到余额)，是用的核销金额
            sheetAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(dataList, PaymentSheetEnum.WRITE_OFF_AMOUNT.getAttr(), 0D);
        } else {
            sheetAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(dataList, PaymentSheetEnum.AMOUNT.getAttr(), 0D);
        }
        SheetAttrPojo sheetAttrPojo = new SheetAttrPojo(XbbRefTypeEnum.PAYMENT_SHEET);
        /*
          经销商类型回款单，需要做如下事情：
          （1）（经销商端判断，此处不判断）上传凭证：线下支付则必填
          （2）支付方式，厂商端操作需后端自动赋值 - handleAmountDetail方法内处理
                代客操作：已核销，预收款
                预收款账户：预收款核销，红冲预收款核销（退到余额）
                线下打款：红冲预收款核销（原Pro的），红冲预收款余额
                另外，红冲已核销与原单一致（关联新建时处理好）
          （3）上游订货单（合同）付款状态更新
         */
        Integer distributorMark = paasFormEntityExt.getDistributorMark();
        paymentSheetSaveDTO.setDistributorMark(distributorMark);
        //处理金额明细
        List<JSONObject> sonPaymentSheetDataList = saasSaveHelp.handleAmountDetail(paymentSheetSaveDTO, sheetAmount, sheetTypeEnum, sheetAttrPojo, dataList, amountDetails, allAmount, contractIdToAmount, paymentIdToAmount, contractIds, paymentIds);
        Long linkPreId = FastJsonHelper.getLongFromFormData(dataList, PaymentSheetEnum.RED_SHEET.getAttr());
        //红冲要对原始回款单关联的已收/部分已收应收款红冲，需要用前端传入的应收款ids，否则需要自己查
        Double sumAmount = getSumAmount(paymentSheetSaveDTO, contractIds, paymentIds, linkPreId);
        //允许误差小于等于ConfigConstant.amountAccuracy
        if (Arith.sub(sheetAmount , sumAmount) > ConfigConstant.amountAccuracy) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.DETAIL_BEYOND_AMOUNT_RED, PaymentSheetEnum.RED_SHEET.getAttrName());
        }
        //处理应收款并封装要更新的结果：判断已收金额是否已经超过应收款内的未收金额，并把应收款的已收金额增加及更改其回款状态等
        HandleLinkInfoVO paymentLinkInfoVO = paymentService.handlePayment(PaymentSheetTypeEnum.RED_WRITE_OFF, dataList, corpid, paymentIdToAmount, paymentIds);
        List<UpdateDataEntity> updatePayList = paymentLinkInfoVO.getUpdateList();
        //处理合同并封装要更新的结果：判断已收金额是否已经超过合同内的未收金额，并把合同的已收金额增加等
        List<UpdateDataEntity> updateConList = contractService.handleContract(distributorMark, PaymentSheetTypeEnum.RED_WRITE_OFF, corpid, contractIdToAmount, contractIds);
//        verifySheetAmount(corpid, paymentId, sheetAmount, paymentAmount);
        //assignPayTime(paymentDataList, dataList);
        PaymentSheetEntityExt paymentSheetEntityExt = new PaymentSheetEntityExt();
        BeanUtil.copyProperties(paasFormDataEntity, paymentSheetEntityExt);
        fundHelp.initSheetEntity4Save(userId, paymentSheetEntityExt);
        for (Map.Entry<String,Object> entry : SaasDataInitHelp.initPaymentSheet().entrySet()){
            SaasDataInitHelp.antDataSet(dataList,entry.getKey(),entry.getValue());
        }
        long now = DateUtil.getNow();
        paymentSheetEntityExt.setData(dataList);
        String uuid = UUID.randomUUID().toString();
        paymentSheetEntityExt.setUuid(uuid);
        paymentSheetEntityExt.setAddTime(now);
        paymentSheetEntityExt.setUpdateTime(now);
        List<PaymentSheetEntityExt> sonSheetList = null;
        Long sheetId = null;
        //红冲预收款核销(退到余额)不入流水
        boolean flowFlag = !redWriteOffBalanceFlag;
        try {
            //插入父回款、子回款
            if (sonPaymentSheetDataList.size() > BasicConstant.ONE) {
                paymentSheetEntityExt.setAlone(SheetAloneEnum.PARENT.getCode());
            } else {
                //没有明细，父子合并
                paymentSheetEntityExt.setAlone(SheetAloneEnum.NORMAL.getCode());
            }
            paymentSheetModel.save(paymentSheetEntityExt);
            sheetId = paymentSheetEntityExt.getId();
            paasFormDataEntity.setId(sheetId);
            sonSheetList = saasPaymentSheetHelper.savePaymentSheetSon(corpid, sonPaymentSheetDataList, paymentSheetEntityExt, uuid, paymentLinkInfoVO);
            //更新应收款/合同
            paymentModel.savePaymentPlan(corpid, updatePayList);
            contractHelp.saveContract(corpid, updateConList, "PaymentSheetServiceImpl.redSave：新建红冲回款单更新合同时失败");
            Map<String, FieldAttrEntity> explainMap = paymentSheetSaveDTO.getExplainMap();
            Double amount = FastJsonHelper.getDoubleOrDefaultFromFormData(dataList, PaymentSheetEnum.AMOUNT.getAttr(), 0D);
            if (redWriteOffBalanceFlag) {
                //红冲预收款核销回款单新建时，增加预收款余额，不影响资金账户
                saasPaymentSheetHelper.saveByPaymentSheet(paymentSheetEntityExt, distributorMark);
            } else {
                Long accountId = FastJsonHelper.getLongOrDefaultFromFormData(dataList, PaymentSheetEnum.FUND_ACCOUNT.getAttr(), 0L);
                Long addTime = paymentSheetEntityExt.getAddTime();
                Long sheetTime = fundHelp.getSheetTime4FundFlow(dataList, PaymentSheetEnum.PAYMENT_TIME.getAttr(), explainMap, addTime);
                //账户余额变动
                payAndPaymentSheetHelp.saveByPaymentSheetOrPaySheet(accountId, amount, corpid);
                //流水表日志记录
                FundAccountFlowEntity fundAccountFlowEntity = new FundAccountFlowEntity(corpid, addTime, paymentSheetEntityExt.getCreatorId(), FundAccountFlowTypeEnum.PAYMENT_SHEET.getCode(), sheetId, accountId, sheetTime, amount);
                fundAccountFlowModel.insert(fundAccountFlowEntity);
            }
            //按最小纬度进项生成客户对账单
            if (sonSheetList.size() > BasicConstant.ONE) {
                saasCustomerStatementHelper.save(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), explainMap, sonSheetList, distributorMark);
            } else {
                saasCustomerStatementHelper.save(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), explainMap, Arrays.asList(paymentSheetEntityExt), distributorMark);
            }
            //处理回款单团队-必须在团队保存后执行，需要用到回款单的id
            saasPaymentSheetHelper.handleSheetTeam(dataList, corpid, sheetTypeEnum, paymentSheetEntityExt, sonSheetList, true);
            // 添加项目日志，及推送消息
            userId = saasSaveHelp.sheetAddLog(paymentSheetSaveDTO, corpid, userId, paymentSheetEntityExt, XbbRefTypeEnum.PAYMENT_SHEET.getName(), OperateModuleTypeEnum.PaymentManage);
            createLogAndMessage(PushTypeEnum.PAYMENT_SHEET_RED_ADD, dataList, corpid, userId, paymentSheetEntityExt, I18nMessageUtil.getMessage(CustomerCommunicateConstant.PAYMENT_SHEET_RED_ADD_CONTRACT), I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_RED_ADD_TITLE), I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_RED_ADD_CONTENT), distributorMark);
        } catch (XbbException e) {
            //回滚子单，流水，对账记录，应收款，合同，预收款余额
            Long customerId = dataList.getLongValue(PaymentSheetEnum.CUSTOMER_ID.getAttr());
            addRollBack4LinkEs(corpid, sheetId, sonSheetList, flowFlag, paymentIds, contractIds, redWriteOffBalanceFlag, Collections.singletonList(customerId), null, false);
            throw e;
        } catch (Exception e) {
            //回滚子单，流水，对账记录，应收款，合同，预收款余额
            Long customerId = dataList.getLongValue(PaymentSheetEnum.CUSTOMER_ID.getAttr());
            addRollBack4LinkEs(corpid, sheetId, sonSheetList, flowFlag, paymentIds, contractIds, redWriteOffBalanceFlag, Collections.singletonList(customerId), null, false);
            LOG.error("保存回款单到数据库报错", e);
            throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208052, PaymentErrorCodeEnum.API_ERROR_208052.getMsg(), XbbRefTypeEnum.PAYMENT_SHEET.getName());
        }
    }

    /**
     * 坏账
     *
     * @param paymentSheetSaveDTO 回款入参
     * @throws XbbException 异常
     */
    @Deprecated
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void badSave(PaymentSheetSaveDTO paymentSheetSaveDTO, PaasFormDataEntity paasFormDataEntity, PaasFormEntityExt paasFormEntityExt) throws XbbException{
        /*
         坏账：回款金额，核销金额一致
         */
        JSONObject dataList = paymentSheetSaveDTO.getDataList();
        String corpid = paymentSheetSaveDTO.getCorpid();
        String userId = paymentSheetSaveDTO.getUserId();
        if (Objects.nonNull(paymentSheetSaveDTO.getProcessCreatorId())) {
            userId = paymentSheetSaveDTO.getProcessCreatorId();
        }
        JSONArray amountDetails = paymentSheetSaveDTO.getAmountDetail();
        //TODO 金额校验，后续修改成根据每条明细判断，暂时未用到
        double allAmount = 0D;
        //应收款和合同对应的回款金额
        Map<Long, Double> contractIdToAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, Double> paymentIdToAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Set<Long> contractIds = new HashSet<>();
        Set<Long> paymentIds = new HashSet<>();
        //回款单的实际坏账金额
        Double sheetAmount = getDoubleOrDefaultFromFormData(dataList, PaymentSheetEnum.AMOUNT.getAttr(), 0D);
        SheetTypeEnum sheetTypeEnum = PaymentSheetTypeEnum.BAD_PAYMENT.getSheetTypeEnum();
        SheetAttrPojo sheetAttrPojo = new SheetAttrPojo(XbbRefTypeEnum.PAYMENT_SHEET);
        /*
          经销商类型回款单，需要做如下事情：
          （1）（经销商端判断，此处不判断）上传凭证：线下支付则必填
          （2）支付方式，厂商端操作需后端自动赋值 - handleAmountDetail方法内处理
                代客操作：已核销，预收款
                预收款账户：预收款核销，红冲预收款核销（退到余额）
                线下打款：红冲预收款核销（原Pro的），红冲预收款余额
                另外，红冲已核销与原单一致（关联新建时处理好）
          （3）上游订货单（合同）付款状态更新
         */
        Integer distributorMark = paasFormEntityExt.getDistributorMark();
        paymentSheetSaveDTO.setDistributorMark(distributorMark);
        //处理金额明细
        List<JSONObject> sonPaymentSheetDataList = saasSaveHelp.handleAmountDetail(paymentSheetSaveDTO, sheetAmount, sheetTypeEnum, sheetAttrPojo, dataList, amountDetails, allAmount, contractIdToAmount, paymentIdToAmount, contractIds, paymentIds);
        Double sumAmount = getSumAmount(paymentSheetSaveDTO, contractIds, paymentIds, null);
        //允许误差小于等于ConfigConstant.amountAccuracy
        if (Arith.sub(sheetAmount , sumAmount) > ConfigConstant.amountAccuracy) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.BAD_AMOUNT, StringUtil.formatDouble(sumAmount));
        }
        //处理应收款并封装要更新的结果：判断已收金额是否已经超过应收款内的未收金额，并把应收款的已收金额增加及更改其回款状态等
        HandleLinkInfoVO paymentLinkInfoVO = paymentService.handlePayment(PaymentSheetTypeEnum.BAD_PAYMENT, dataList, corpid, paymentIdToAmount, paymentIds);
        List<UpdateDataEntity> updatePayList = paymentLinkInfoVO.getUpdateList();
        //处理合同并封装要更新的结果：判断已收金额是否已经超过合同内的未收金额，并把合同的已收金额增加等
        List<UpdateDataEntity> updateConList = contractService.handleContract(distributorMark, PaymentSheetTypeEnum.BAD_PAYMENT, corpid, contractIdToAmount, contractIds);
        // 保存坏账回款单
        PaymentSheetEntityExt paymentSheetEntityExt = new PaymentSheetEntityExt();
        BeanUtil.copyProperties(paasFormDataEntity, paymentSheetEntityExt);
        for (Map.Entry<String,Object> entry : SaasDataInitHelp.initPaymentSheet().entrySet()){
            SaasDataInitHelp.antDataSet(dataList,entry.getKey(),entry.getValue());
        }
        paymentSheetEntityExt.setData(dataList);
        paymentSheetEntityExt.setId(null);
        String uuid = UUID.randomUUID().toString();
        paymentSheetEntityExt.setUuid(uuid);
        paymentSheetEntityExt.setAddTime(DateUtil.getNow());
        paymentSheetEntityExt.setUpdateTime(DateUtil.getNow());
        List<PaymentSheetEntityExt> sonSheetList = null;
        Long sheetId = null;
        try {
            //插入父回款、子回款
            if (sonPaymentSheetDataList.size() > BasicConstant.ONE) {
                paymentSheetEntityExt.setAlone(SheetAloneEnum.PARENT.getCode());
            } else {
                //没有明细，父子合并
                paymentSheetEntityExt.setAlone(SheetAloneEnum.NORMAL.getCode());
            }
            paymentSheetModel.save(paymentSheetEntityExt);
            sheetId = paymentSheetEntityExt.getId();
            //回滚ES 需要dataID
            paasFormDataEntity.setId(sheetId);
            sonSheetList = saasPaymentSheetHelper.savePaymentSheetSon(corpid, sonPaymentSheetDataList, paymentSheetEntityExt, uuid, paymentLinkInfoVO);
            paymentModel.savePaymentPlan(corpid, updatePayList);
            contractHelp.saveContract(corpid, updateConList, "PaymentSheetServiceImpl.badSave：新建坏账回款单更新合同时失败");
            //按最小纬度进项生成客户对账单
            if (sonSheetList.size() > BasicConstant.ONE) {
                saasCustomerStatementHelper.save(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), paymentSheetSaveDTO.getExplainMap(), sonSheetList, distributorMark);
            } else {
                saasCustomerStatementHelper.save(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), paymentSheetSaveDTO.getExplainMap(), Arrays.asList(paymentSheetEntityExt), distributorMark);
            }
            //处理回款单团队-必须在团队保存后执行，需要用到回款单的id
            saasPaymentSheetHelper.handleSheetTeam(dataList, corpid, sheetTypeEnum, paymentSheetEntityExt, sonSheetList, true);
            // 添加项目日志，及推送消息
            userId = saasSaveHelp.sheetAddLog(paymentSheetSaveDTO, corpid, userId, paymentSheetEntityExt, I18nMessageUtil.getMessage(I18nStringConstant.BAD_DEBT) + XbbRefTypeEnum.PAYMENT_SHEET.getName(), OperateModuleTypeEnum.PaymentManage);
            createLogAndMessage(PushTypeEnum.PAYMENT_SHEET_BAD_ADD, dataList, corpid, userId, paymentSheetEntityExt, I18nMessageUtil.getMessage(CustomerCommunicateConstant.PAYMENT_SHEET_BAD_CONTRACT), I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_BAD_ADD_TITLE), I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_BAD_ADD_CONTENT), distributorMark);
        } catch (XbbException e) {
            //回滚子单，对账记录，应收款，合同
            addRollBack4LinkEs(corpid, sheetId, sonSheetList, false, paymentIds, contractIds, false, null, null, false);
            throw e;
        } catch (Exception e) {
            //回滚子单，对账记录，应收款，合同
            addRollBack4LinkEs(corpid, sheetId, sonSheetList, false, paymentIds, contractIds, false, null, null, false);
            LOG.error("插入坏账回款单失败, e");
            throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208052, PaymentErrorCodeEnum.API_ERROR_208052.getMsg(), XbbRefTypeEnum.PAYMENT_SHEET.getName());
        }
    }

    /**
     * 新建回款单失败，回滚关联业务的es数据
     * 回滚子单，流水(无流水的不需回滚)，对账记录，应收款，合同，预收款余额(未处理余额则无需回滚)，回款计划
     * @param corpid 公司id
     * @param sheetId 回款单id（即新建的这条回款单id），当前该参数控制回滚对账单数据、流水数据
     * @param sonSheetList 子单
     * @param flowFlag 是否需要回滚资金流水es
     * @param paymentIdIn 关联应收款id集合
     * @param contractIdIn 关联合同id集合
     * @param balanceFlag 是否需要回滚预收款余额es
     * @param customerIdIn 客户id集合
     * @param taskIdIn 要完成的回款计划id集合
     * @param creditRollBack 是否需要回滚信用流水
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void addRollBack4LinkEs(String corpid, Long sheetId, List<PaymentSheetEntityExt> sonSheetList, boolean flowFlag, Collection<Long> paymentIdIn, Collection<Long> contractIdIn, boolean balanceFlag, Collection<Long> customerIdIn, Collection<Long> taskIdIn, boolean creditRollBack) {
        //新建失败，回滚异步
        threadPoolBeanConfig.addUpdateDelThreadPool().execute(() -> {
            try{
                //回滚子回款单es--新增是直接物理删除es（主表和团队表数据在主流程内已经回滚）
                addRollbackList(sonSheetList, corpid);
                //回滚流水es-直接删除es，预收款核销不入流水，不用处理
                if (flowFlag) {
                    fundAccountFlowService.addRollback(corpid, FundAccountFlowTypeEnum.PAYMENT_SHEET.getCode(), Collections.singletonList(sheetId));
                }
                //回滚对账记录es，所有类型回款单都入对账记录，因此根据sheetId判断即可
                customerStatementService.addRollback(corpid, XbbRefTypeEnum.PAYMENT_SHEET.getCode(), Collections.singletonList(sheetId));
                /*
                 以下业务需要查数据库同步至es内，因此睡眠一小会，确保事务回滚完毕数据库恢复到操作之前的状态
                 */
                Thread.sleep(1000L);
                //被更新的关联业务es回滚至更改之前（通过查数据库）
                paymentService.updateRollback(paymentIdIn, corpid);
                contractService.updateRollback(contractIdIn, corpid);
                //回滚预收款余额es-如果新增的预收款余额，则删除es；如果原是对原预收款余额修改，则读库同步至es
                if (balanceFlag) {
                    paymentBalanceService.updateRollbackByCustomerId(customerIdIn, corpid);
                }
                if (creditRollBack) {
                    creditCardBillService.addRollback(corpid, CreditCardBillTypeEnum.PAYMENT_SHEET, sheetId);
                }
            } catch (XbbException | InterruptedException e1) {
                LOG.error("addRollBack4LinkEs rollback" + e1);
                // 恢复中断状态
                Thread.currentThread().interrupt();
            }
        });
    }

    /**
     * 回款单删除失败回滚es，同步数据库数据至es（需确保失败后数据库事务回滚结束才同步es）
     * @param corpid 公司id
     * @param allDelIdList 要删除的回款单id集合
     * @param paymentIdIn 关联应收款id集合
     * @param contractIdIn 关联合同id集合
     * @param customerIdSet 客户id集合
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void delRollBack4LinkEs(String corpid, List<Long> allDelIdList, List<Long> paymentIdIn, List<Long> contractIdIn, Set<Long> customerIdSet) {
        threadPoolBeanConfig.addUpdateDelThreadPool().execute(() -> {
            try{
                //需要查数据库同步至es内，因此睡眠一小会，确保事务回滚完毕数据库恢复到操作之前的状态
                Thread.sleep(1000L);
                //以下为同步数据库数据至es内：通过关联业务id，查询回滚后的数据库数据，同步至es内
                updateRollback(allDelIdList, corpid);
                //回滚资金流水、对账记录、关联应收款、关联合同、预收款余额，不处理完成回款计划（因为删除本身也不会去更改完成的相关回款计划）
                fundAccountFlowService.updateRollback(corpid, FundAccountFlowTypeEnum.PAYMENT_SHEET.getCode(), allDelIdList);
                customerStatementService.updateRollback(corpid, XbbRefTypeEnum.PAYMENT_SHEET.getCode(), allDelIdList);
                paymentService.updateRollback(paymentIdIn, corpid);
                contractService.updateRollback(contractIdIn, corpid);
                paymentBalanceService.updateRollbackByCustomerId(customerIdSet, corpid);
            } catch (XbbException | InterruptedException e1) {
                LOG.error("delRollBack4LinkEs rollback" + e1);
                // 恢复中断状态
                Thread.currentThread().interrupt();
            }
        });
    }



    @Override
    public void formatLinkAddWriteOffPrepayment(HandlerExplainInLinkItemDTO handlerExplainInLinkItemDTO) throws XbbException {
        JSONObject linkFormData = handlerExplainInLinkItemDTO.getLinkFormData();
        Long linkDataId = handlerExplainInLinkItemDTO.getLinkDataId();
        List<FieldAttrEntity> explainList = handlerExplainInLinkItemDTO.getExplainList();
        String corpid = handlerExplainInLinkItemDTO.getCorpid();
        FieldAttrEntity timeField = new FieldAttrEntity();
        FieldAttrEntity customerField = new FieldAttrEntity();
        FieldAttrEntity contractField = new FieldAttrEntity();
        FieldAttrEntity paymentField = new FieldAttrEntity();
        for(FieldAttrEntity fieldAttrEntity :explainList) {
            if (Objects.equals(fieldAttrEntity.getAttr(), PaymentSheetEnum.PAYMENT_TIME.getAttr())) {
                timeField = fieldAttrEntity;
            } else if (Objects.equals(fieldAttrEntity.getAttr(), PaymentSheetEnum.CUSTOMER_ID.getAttr())) {
                customerField = fieldAttrEntity;
            } else if (Objects.equals(fieldAttrEntity.getAttr(), PaymentSheetEnum.CONTRACT.getAttr())) {
                contractField = fieldAttrEntity;
            } else if (Objects.equals(fieldAttrEntity.getAttr(), PaymentSheetEnum.PAYMENT.getAttr())) {
                paymentField = fieldAttrEntity;
            }
        }
        RedundantTemplateTypeEnum redundantTemplateTypeEnum = RedundantTemplateTypeEnum.getByCode(handlerExplainInLinkItemDTO.getLinkBusinessType());
        Long customerId = 0L;
        //核销预收款需要根据返回当前客户下的预收款余额
        PaymentBalanceEntity paymentBalanceEntity;
        switch (redundantTemplateTypeEnum) {
            case CONTRACT:
                JSONArray contractCustomer = FastJsonHelper.getJsonArrOrDefaultFromFormData(linkFormData, ContractEnum.LINK_CUSTOMER.getAttr(), new JSONArray());
                customerId = contractCustomer.getJSONObject(0).getLong(BasicConstant.ID);
                paymentBalanceEntity = paymentBalanceModel.getByCustomerId(customerId, corpid);
                customerField.setDefaultAttr(saasUpdateHelp.getDefaultAttrPojoByPaymentBalance(contractCustomer, paymentBalanceEntity));
                String contractId = handlerExplainInLinkItemDTO.getPaasFormDataEntityExt().getSerialNo();
                contractField.setDefaultAttr(saasUpdateHelp.getDefaultAttrPojo(linkDataId, contractId));
                break;
            case PAYMENT:
                JSONArray paymentCustomer = FastJsonHelper.getJsonArrOrDefaultFromFormData(linkFormData, PaymentEnum.CUSTOMER.getAttr(), new JSONArray());
                customerId = paymentCustomer.getJSONObject(0).getLong(BasicConstant.ID);
                paymentBalanceEntity = paymentBalanceModel.getByCustomerId(customerId, corpid);
                customerField.setDefaultAttr(saasUpdateHelp.getDefaultAttrPojoByPaymentBalance(paymentCustomer, paymentBalanceEntity));
                // 归属人
                JSONArray contractArray = FastJsonHelper.getJsonArrOrDefaultFromFormData(linkFormData, PaymentEnum.CONTRACT.getAttr(), new JSONArray());
                contractField.setDefaultAttr(saasUpdateHelp.getDefaultAttrPojo(contractArray));
                paymentField.setDefaultAttr(saasUpdateHelp.getDefaultAttrPojo(linkDataId, handlerExplainInLinkItemDTO.getPaasFormDataEntityExt().getSerialNo()));
                break;
            case CUSTOMER:
                String customerName = linkFormData.getString(CustomerManagementEnum.NAME.getAttr());
                customerId = linkDataId;
                paymentBalanceEntity = paymentBalanceModel.getByCustomerId(customerId, corpid);
                customerField.setDefaultAttr(saasUpdateHelp.getDefaultAttrPojoByPaymentBalance(linkDataId, customerName, paymentBalanceEntity));
                break;
            default:
                throw new XbbException(TemplateErrorCodeEnum.API_ERROR_350006);
        }
        // 日期
        DefaultAttrPoJo timeDefaultAttrPoJo = timeField.getDefaultAttr();
        if (Objects.isNull(timeDefaultAttrPoJo)) {
            timeDefaultAttrPoJo = new DefaultAttrPoJo();
        }
        if (StringUtil.isEmpty(timeDefaultAttrPoJo.getDefaultType())) {
            timeDefaultAttrPoJo.setDefaultType(DefaultTypeEnum.DATE_BY_BACK_END.getAlias());
        }
        timeDefaultAttrPoJo.setDefaultValue(DateTimeUtil.getInt());
        timeField.setDefaultAttr(timeDefaultAttrPoJo);
        timeField.setAttrName(SheetAssembleEnum.WriteOffPrepayment.PAYMENT_TIME.getName());
    }

    @Override
    public List<PaasFormDataEntityExt> getPaymentSheetEsDataListByPaymentId(String corpid, Long paymentId) throws XbbException {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryBuilder.filter(termQuery("data." + PaymentSheetEnum.PAYMENT.getAttr(), paymentId));
        boolQueryBuilder.filter(termQuery("del", 0));
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET.getType()));
        sourceBuilder.query(boolQueryBuilder);
        SearchRequest searchRequest = new SearchRequest(IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET.getIndex());
        PageRequest pageRequest = EsUtil.setPage(sourceBuilder, 0, PaasConstant.ES_MAX_PAGE_SIZE);
        searchRequest.source(sourceBuilder);
        XbbAggregatedPage<PaasFormDataEntityExt> esEntities = xbbElasticsearchRestTemplate.queryForPages(pageRequest, searchRequest, PaasFormDataEntityExt.class);
        return esEntities.getContent();
    }

    @Override
    public List<PaasFormDataEntityExt> getPaymentSheetEsDataListByCustomerIds(String corpid, List<Long> customerIds, List<String> typeList) throws XbbException {
        return paymentSheetModel.getPaymentSheetEsDataListByCustomerIds(corpid, customerIds, typeList);
    }

    @Override
    public List<PaasFormDataEntityExt> getPaymentSheetEsDataListByCustomerId(String corpid, Long customerId, List<String> codeList, UserVO loginUser) throws XbbException {
       return paymentSheetModel.getPaymentSheetEsDataListByCustomerId(corpid, customerId, codeList, loginUser);
    }

    @Override
    public void beforeSave(ValidateDataDTO validateDataDTO) throws XbbException {
        paymentSheetAnalyticalService.beforeSave(validateDataDTO);
    }

    /**
     * 不走工作流的回款单保存的afterSave统一入口
     *
     * @param saasFormSaveDTO saas保存数据
     * @throws XbbException
     */
    @Override
    public void afterSave(SaasFormSaveDTO saasFormSaveDTO) throws XbbException {
        Integer businessType = saasFormSaveDTO.getBusinessType();
        PaasFormDataEntity newPaasFormDataEntity = saasFormSaveDTO.getNewPaasFormDataEntity();
        if (Objects.equals(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), businessType)) {
            //已核销
            writeOffAfterSave(saasFormSaveDTO);
        } else if (Objects.equals(XbbRefTypeEnum.WRITE_OFF_PREPAYMENT.getCode(), businessType)) {
            //预收款核销
            writeOffAfterSave(saasFormSaveDTO);
        } else if (Objects.equals(XbbRefTypeEnum.PREPAYMENT.getCode(), businessType)) {
            //预收款
            prepaymentAfterSave(saasFormSaveDTO);
        } else if (Objects.equals(XbbRefTypeEnum.RED_PREPAYMENT.getCode(), businessType)) {
            //红冲预收款
            prepaymentAfterSave(saasFormSaveDTO);
        } else if (Objects.equals(XbbRefTypeEnum.RED_PAYMENT_SHEET.getCode(), businessType)) {
            //红冲已核销
            redAfterSave(saasFormSaveDTO);
        } else if (Objects.equals(XbbRefTypeEnum.RED_WRITE_OFF_PREPAYMENT.getCode(), businessType)) {
            //红冲预收款核销
            redAfterSave(saasFormSaveDTO);
        } else if (Objects.equals(XbbRefTypeEnum.RED_WRITE_OFF_BALANCE.getCode(), businessType)) {
            //红冲预收款核销退到余额
            redAfterSave(saasFormSaveDTO);
        } else if (Objects.equals(XbbRefTypeEnum.BAD_DEBT.getCode(), businessType)) {
            //坏账
            badAfterSave(saasFormSaveDTO);
        }
        //这里需要重置退货退款里面的回款单信息
        Long refundId = saasFormSaveDTO.getRefundId();
        if (refundId != null) {
            saasRefundHelper.updateRefundPaymentSheetId(refundId, newPaasFormDataEntity, saasFormSaveDTO.getCorpid());
        }
    }

    /**
     * 坏账类型回款单
     *
     * @param saasFormSaveDTO
     */
    private void badAfterSave(SaasFormSaveDTO saasFormSaveDTO) throws XbbException {
        Integer businessType = saasFormSaveDTO.getBusinessType();
        String corpid = saasFormSaveDTO.getCorpid();
        String userId = saasFormSaveDTO.getUserId();
        Integer distributorMark = saasFormSaveDTO.getDistributorMark();
        PaasFormDataEntity newPaasFormDataEntity = saasFormSaveDTO.getNewPaasFormDataEntity();
        PaymentSheetEntityExt paymentSheetEntityExt = new PaymentSheetEntityExt();
        BeanUtil.copyProperties(newPaasFormDataEntity, paymentSheetEntityExt);
        JSONObject newData = newPaasFormDataEntity.getData();
        PaymentSheetTypeEnum paymentSheetTypeEnum = PaymentSheetTypeEnum.getByBusinessType(businessType);
        SheetTypeEnum sheetTypeEnum = paymentSheetTypeEnum.getSheetTypeEnum();
        SheetAttrPojo sheetAttrPojo = new SheetAttrPojo(XbbRefTypeEnum.PAYMENT_SHEET);
        //回款单的实收回款金额
        Double sheetAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(newData, PaymentSheetEnum.AMOUNT.getAttr(), 0D);
        double allAmount = 0D;
        Set<Long> contractIds = new HashSet<>();
        Set<Long> paymentIds = new HashSet<>();
        List<PaymentSheetEntityExt> sonSheetList = null;
        Map<String, FieldAttrEntity> explainMap = saasFormSaveDTO.getExplainMap();
        //应收款和合同对应的回款金额
        Map<Long, Double> contractIdToAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, Double> paymentIdToAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);

        //处理金额明细
        List<JSONObject> sonPaymentSheetDataList = saasSaveHelper.handleAmountDetail(saasFormSaveDTO, sheetAmount, sheetTypeEnum, sheetAttrPojo, newData, saasFormSaveDTO.getAmountDetail(), allAmount, contractIdToAmount, paymentIdToAmount, contractIds, paymentIds);
        //处理应收款并封装要更新的结果：判断已收金额是否已经超过应收款内的未收金额，并把应收款的已收金额增加及更改其回款状态等
        HandleLinkInfoVO paymentLinkInfoVO = saasPaymentHelper.handlePayment(PaymentSheetTypeEnum.BAD_PAYMENT, newData, corpid, paymentIdToAmount, paymentIds);
        List<UpdateDataEntity> updatePayList = paymentLinkInfoVO.getUpdateList();
        //处理合同并封装要更新的结果：判断已收金额是否已经超过合同内的未收金额，并把合同的已收金额增加等
        List<UpdateDataEntity> updateConList = contractHelp.handleContract(distributorMark, PaymentSheetTypeEnum.BAD_PAYMENT, corpid, contractIdToAmount, contractIds);
        //插入回款单子回款单
        sonSheetList = saasPaymentSheetHelper.savePaymentSheetSon(corpid, sonPaymentSheetDataList,  paymentSheetEntityExt, paymentSheetEntityExt.getUuid(), paymentLinkInfoVO);
        //更新应收款/合同
        paymentModel.savePaymentPlan(corpid, updatePayList);
        contractHelp.saveContract(corpid, updateConList, "PaymentSheetServiceImpl.redSave：新建红冲回款单更新合同时失败");
        //按最小纬度进项生成客户对账单
        if (sonSheetList.size() > BasicConstant.ONE) {
            saasCustomerStatementHelper.save(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), explainMap, sonSheetList, distributorMark);
        } else {
            saasCustomerStatementHelper.save(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), explainMap, Arrays.asList(paymentSheetEntityExt), distributorMark);
        }
        //处理回款单团队-必须在团队保存后执行，需要用到回款单的id
        operateSheetTeam(paymentSheetEntityExt, sonSheetList, true, newPaasFormDataEntity, saasFormSaveDTO.getSaasNeedRedundantAttrPoJo(), saasFormSaveDTO.getIsImport(), false);
        //saasPaymentSheetHelper.handleSheetTeam(newData, corpid, sheetTypeEnum, paymentSheetEntityExt, sonSheetList, true);
        //添加推送消息
        if (!Objects.equals(DistributorMarkEnum.DISTRIBUTOR.getCode(), distributorMark)) {
            try {
                PaymentSheetDynamicStrategy paymentSheetDynamicStrategy = dynamicStrategyFactory.getPaymentSheetDynamicStrategyByBusinessType(businessType);
                transactionHelp.afterCommitOrCommitNow(p->{
                    paymentSheetDynamicStrategy.bad(DynamicPaymentSheetDTO.initDynamicPaymentSheetDTO(paymentSheetEntityExt.getDataId(), paymentSheetEntityExt.getFormId(), saasFormSaveDTO,
                            userModel.getByKeyIngoreDel(userId, corpid)));
                });
            } catch (Exception e) {
                LOG.error("com.xbongbong.saas.service.impl.PaymentSheetServiceImpl.badAfterSave", e);
            }
        }
        createLogAndMessage(PushTypeEnum.PAYMENT_SHEET_BAD_ADD, newData, corpid, userId, paymentSheetEntityExt, I18nMessageUtil.getMessage(CustomerCommunicateConstant.PAYMENT_SHEET_BAD_CONTRACT), I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_BAD_ADD_TITLE), I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_BAD_ADD_CONTENT), distributorMark);
    }

    /**
     * 操作回款单团队
     *
     * @param paymentSheetEntityExt
     * @param sheetList
     * @param ignoreMainSheet
     * @param paasFormDataEntity
     * @param saasNeedRedundantAttrPojo
     * @param isImport
     */
    @Override
    public AfterSavePojo operateSheetTeam(PaymentSheetEntityExt paymentSheetEntityExt, List<PaymentSheetEntityExt> sheetList,
                                  boolean ignoreMainSheet, PaasFormDataEntity paasFormDataEntity, SaasNeedRedundantAttrPojo saasNeedRedundantAttrPojo, Integer isImport, Boolean workFlowTag) throws XbbException {
        if (ignoreMainSheet) {
            sheetList.add(paymentSheetEntityExt);
        }
        Map<Long, List<Long>> sheetIdAndDataIdMap = new HashMap<>(sheetList.size());
        List<Long> contractIdIn = new ArrayList<>();
        String attrCon = PaymentSheetEnum.CONTRACT.getAttr();
        for (PaymentSheetEntityExt paymentEntityExt : sheetList){
            fundHelp.handleSheetIdAndContractIdMap(contractIdIn, sheetIdAndDataIdMap, paymentEntityExt, attrCon);
        }
        AfterSavePojo afterSavePojo = teamDataHelp.savePayOrPaymentSheetUserTeam(saasNeedRedundantAttrPojo, paasFormDataEntity, isImport, sheetList,
                null, workFlowTag, true);
        return afterSavePojo;
    }

    @Override
    public void afterSaveForUpdateWorkflow(SaasFormSaveDTO saasFormSaveDTO) throws XbbException {
        PaasFormDataEntity newPaasFormDataEntity = saasFormSaveDTO.getNewPaasFormDataEntity();
        teamDataHelp.saveUserTeam(saasFormSaveDTO.getSaasNeedRedundantAttrPoJo(),saasFormSaveDTO.getNewPaasFormDataEntity().getId(), newPaasFormDataEntity,saasFormSaveDTO.getIsImport(),true);
    }


    /**
     * 红冲已核销、红冲预收款核销、红冲预收款核销退到余额
     *
     * @param saasFormSaveDTO
     */
    private void redAfterSave(SaasFormSaveDTO saasFormSaveDTO) throws XbbException {
        String userId = saasFormSaveDTO.getUserId();
        Integer businessType = saasFormSaveDTO.getBusinessType();
        String corpid = saasFormSaveDTO.getCorpid();
        Integer distributorMark = saasFormSaveDTO.getDistributorMark();
        OnlinePaymentFlowEntity onlinePaymentFlowEntity = saasFormSaveDTO.getOnlinePaymentFlowEntity();
        PaasFormDataEntity newPaasFormDataEntity = saasFormSaveDTO.getNewPaasFormDataEntity();
        PaymentSheetEntityExt paymentSheetEntityExt = new PaymentSheetEntityExt();
        BeanUtil.copyProperties(newPaasFormDataEntity, paymentSheetEntityExt);
        JSONObject newData = newPaasFormDataEntity.getData();
        PaymentSheetTypeEnum paymentSheetTypeEnum = PaymentSheetTypeEnum.getByBusinessType(businessType);
        SheetTypeEnum sheetTypeEnum = paymentSheetTypeEnum.getSheetTypeEnum();
        SheetAttrPojo sheetAttrPojo = new SheetAttrPojo(XbbRefTypeEnum.PAYMENT_SHEET);
        Long sheetId = newPaasFormDataEntity.getDataId();
        //回款单的实收回款金额
        Double sheetAmount;
        Double amount;
        double allAmount = 0D;
        boolean redWriteOffBalanceFlag = SheetTypeEnum.onlyWriteOffAmount(paymentSheetTypeEnum.getCode());
        Set<Long> contractIds = new HashSet<>();
        Set<Long> paymentIds = new HashSet<>();
        if (redWriteOffBalanceFlag) {
            //预收款核销，是用的核销金额
            sheetAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(newData, PaymentSheetEnum.WRITE_OFF_AMOUNT.getAttr(), 0D);
        } else {
            sheetAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(newData, PaymentSheetEnum.AMOUNT.getAttr(), 0D);
        }
        amount = Arith.mul(sheetAmount, -1);
        List<PaymentSheetEntityExt> sonSheetList = null;
        Map<String, FieldAttrEntity> explainMap = saasFormSaveDTO.getExplainMap();
        //应收款和合同对应的回款金额
        Map<Long, Double> contractIdToAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, Double> paymentIdToAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (Objects.nonNull(onlinePaymentFlowEntity)) {
            //在线支付回调成功的时候需要去更新在线支付流水时间
            onlinePaymentFlowEntity.getData().put(OnlinePaymentFlowDistributorEnum.SHEET_ID.getAttr(), Arrays.asList(newPaasFormDataEntity.getId()));
            onlinePaymentFlowEntity.getData().put(OnlinePaymentFlowDistributorEnum.SHEET_ID_LINK_TEXT.getAttr(), Arrays.asList(newPaasFormDataEntity.getSerialNo()));
            onlinePaymentFlowModel.update(onlinePaymentFlowEntity);
        }
        //处理金额明细
        List<JSONObject> sonPaymentSheetDataList = saasSaveHelper.handleAmountDetail(saasFormSaveDTO, amount, sheetTypeEnum, sheetAttrPojo, newData, saasFormSaveDTO.getAmountDetail(), allAmount, contractIdToAmount, paymentIdToAmount, contractIds, paymentIds);
        //处理应收款并封装要更新的结果：判断已收金额是否已经超过应收款内的未收金额，并把应收款的已收金额增加及更改其回款状态等
        HandleLinkInfoVO paymentLinkInfoVO = saasPaymentHelper.handlePayment(PaymentSheetTypeEnum.RED_WRITE_OFF, newData, corpid, paymentIdToAmount, paymentIds);
        List<UpdateDataEntity> updatePayList = paymentLinkInfoVO.getUpdateList();
        //处理合同并封装要更新的结果：判断已收金额是否已经超过合同内的未收金额，并把合同的已收金额增加等
        List<UpdateDataEntity> updateConList = contractHelp.handleContract(distributorMark, PaymentSheetTypeEnum.RED_WRITE_OFF, corpid, contractIdToAmount, contractIds);
        //插入回款单子回款单
        sonSheetList = saasPaymentSheetHelper.savePaymentSheetSon(corpid, sonPaymentSheetDataList,  paymentSheetEntityExt, paymentSheetEntityExt.getUuid(), paymentLinkInfoVO);
        //更新应收款/合同
        paymentModel.savePaymentPlan(corpid, updatePayList);
        contractHelp.saveContract(corpid, updateConList, "PaymentSheetServiceImpl.redSave：新建红冲回款单更新合同时失败");
        if (redWriteOffBalanceFlag) {
            //核销预收款回款单新建时，减少预收款余额
            saasPaymentSheetHelper.saveByPaymentSheet(paymentSheetEntityExt, distributorMark);
        }else {
            Long addTime = paymentSheetEntityExt.getAddTime();
            Long accountId = FastJsonHelper.getLongOrDefaultFromFormData(newData, PaymentSheetEnum.FUND_ACCOUNT.getAttr(), 0L);
            Long sheetTime = fundHelp.getSheetTime4FundFlow(newData, PaymentSheetEnum.PAYMENT_TIME.getAttr(), explainMap, addTime);
            //账户余额变动
            payAndPaymentSheetHelp.saveByPaymentSheetOrPaySheet(accountId, sheetAmount, corpid);
            //流水表日志记录
            FundAccountFlowEntity fundAccountFlowEntity = new FundAccountFlowEntity(corpid, addTime, paymentSheetEntityExt.getCreatorId(), FundAccountFlowTypeEnum.PAYMENT_SHEET.getCode(), sheetId, accountId, sheetTime, sheetAmount);
            fundAccountFlowModel.insert(fundAccountFlowEntity);
        }
        //按最小纬度进项生成客户对账单
        if (sonSheetList.size() > BasicConstant.ONE) {
            saasCustomerStatementHelper.save(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), explainMap, sonSheetList, distributorMark);
        } else {
            //红冲类型的回款单保存对账流水时，核销金额和回款金额应该置位负数
            double paymentSheetAmount = newData.getDoubleValue(PaymentSheetEnum.AMOUNT.getAttr());
            double writeOffAmount = newData.getDoubleValue(PaymentSheetEnum.WRITE_OFF_AMOUNT.getAttr());
            newData.put(PaymentSheetEnum.AMOUNT.getAttr(),Arith.mul(Math.abs(paymentSheetAmount), -1));
            newData.put(PaymentSheetEnum.WRITE_OFF_AMOUNT.getAttr(),Arith.mul(Math.abs(writeOffAmount), -1));
            saasCustomerStatementHelper.save(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), explainMap, Arrays.asList(paymentSheetEntityExt), distributorMark);
        }
        //处理回款单团队-必须在团队保存后执行，需要用到回款单的id
        operateSheetTeam(paymentSheetEntityExt, sonSheetList, true, newPaasFormDataEntity, saasFormSaveDTO.getSaasNeedRedundantAttrPoJo(), saasFormSaveDTO.getIsImport(), false);
        //saasPaymentSheetHelper.handleSheetTeam(newData, corpid, sheetTypeEnum, paymentSheetEntityExt, sonSheetList, true);
        // 添加项目日志，及推送消息
        if (!Objects.equals(DistributorMarkEnum.DISTRIBUTOR.getCode(), distributorMark)) {
            try {
                PaymentSheetDynamicStrategy paymentSheetDynamicStrategy = dynamicStrategyFactory.getPaymentSheetDynamicStrategyByBusinessType(businessType);
                transactionHelp.afterCommitOrCommitNow(p->{
                    paymentSheetDynamicStrategy.red(DynamicPaymentSheetDTO.initDynamicPaymentSheetDTO(paymentSheetEntityExt.getDataId(), paymentSheetEntityExt.getFormId(), saasFormSaveDTO,
                            userModel.getByKeyIngoreDel(userId, corpid)));
                });
            } catch (Exception e) {
                LOG.error("com.xbongbong.saas.service.impl.PaymentSheetServiceImpl.redAfterSave", e);
            }
        }
        createLogAndMessage(PushTypeEnum.PAYMENT_SHEET_RED_ADD, newData, corpid, userId, paymentSheetEntityExt, I18nMessageUtil.getMessage(CustomerCommunicateConstant.PAYMENT_SHEET_RED_ADD_CONTRACT), I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_RED_ADD_TITLE), I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_RED_ADD_CONTENT), distributorMark);
    }

    /**
     * 预收款类型的回款单的afterSave方法
     *
     * @param saasFormSaveDTO
     */
    private void prepaymentAfterSave(SaasFormSaveDTO saasFormSaveDTO) throws XbbException {
        PaasFormDataEntity newPaasFormDataEntity = saasFormSaveDTO.getNewPaasFormDataEntity();
        Integer distributorMark = saasFormSaveDTO.getDistributorMark();
        Integer businessType = saasFormSaveDTO.getBusinessType();
        JSONObject dataList = newPaasFormDataEntity.getData();
        String corpid = saasFormSaveDTO.getCorpid();
        Long customerId = dataList.getLong(PaymentSheetEnum.CUSTOMER_ID.getAttr());
        String userId = saasFormSaveDTO.getUserId();
        PaymentSheetTypeEnum paymentSheetTypeEnum = PaymentSheetTypeEnum.getByBusinessType(businessType);
        SheetTypeEnum sheetTypeEnum = paymentSheetTypeEnum.getSheetTypeEnum();
        boolean redFlag = false;
        if (Objects.equals(sheetTypeEnum, SheetTypeEnum.RED_PREPAYMENT)) {
            redFlag = true;
        }
        PaymentSheetEntityExt paymentSheetEntityExt = new PaymentSheetEntityExt();
        BeanUtil.copyProperties(newPaasFormDataEntity, paymentSheetEntityExt);
        //添加预收款余额
        paymentBalanceService.saveByPaymentSheet(paymentSheetEntityExt, distributorMark);

        Map<String, FieldAttrEntity> explainMap = saasFormSaveDTO.getExplainMap();
        Long accountId = FastJsonHelper.getLongOrDefaultFromFormData(dataList, PaymentSheetEnum.FUND_ACCOUNT.getAttr(), 0L);
        Long addTime = paymentSheetEntityExt.getAddTime();
        Long sheetTime = fundHelp.getSheetTime4FundFlow(dataList, PaymentSheetEnum.PAYMENT_TIME.getAttr(), explainMap, addTime);
        Double amount =  FastJsonHelper.getDoubleOrDefaultFromFormData(dataList, PaymentSheetEnum.AMOUNT.getAttr(), 0D);
        //账户余额变动
        payAndPaymentSheetHelp.saveByPaymentSheetOrPaySheet(accountId, amount, corpid);
        //流水表日志记录
        FundAccountFlowEntity fundAccountFlowEntity = new FundAccountFlowEntity(corpid, addTime, paymentSheetEntityExt.getCreatorId(), FundAccountFlowTypeEnum.PAYMENT_SHEET.getCode(), paymentSheetEntityExt.getId(), accountId, sheetTime, amount);
        fundAccountFlowModel.insert(fundAccountFlowEntity);
        //处理回款单团队-必须在团队保存后执行，需要用到回款单的id
        operateSheetTeam(paymentSheetEntityExt, new ArrayList<>(), true, newPaasFormDataEntity, saasFormSaveDTO.getSaasNeedRedundantAttrPoJo(), saasFormSaveDTO.getIsImport(), false);
        //处理回款单团队-必须在团队保存后执行，需要用到回款单的id
        //按最小纬度进项生成客户对账单
        List<PaasFormDataEntityExt> paymentSheetInsertList = new ArrayList<>();
        paymentSheetInsertList.add(paymentSheetEntityExt);
        //记录客户对账单
        saasCustomerStatementHelper.save(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), explainMap, paymentSheetInsertList, distributorMark);
        // 添加项目日志，及推送消息
        PaymentSheetSaveDTO paymentSheetSaveDTO = new PaymentSheetSaveDTO();
        BeanUtil.copyProperties(saasFormSaveDTO, paymentSheetSaveDTO);
        userId = saasSaveHelp.sheetAddLog(paymentSheetSaveDTO, corpid, userId, paymentSheetEntityExt, XbbRefTypeEnum.PAYMENT_SHEET.getName(), OperateModuleTypeEnum.PaymentManage);
        PushTypeEnum pushTypeEnum;
        String logMemo;
        String pushTitle;
        String pushContent;
        XbbRefTypeEnum xbbRefTypeEnum;
        if (redFlag) {
            pushTypeEnum = PushTypeEnum.PAYMENT_SHEET_RED_PREPAYMENT_ADD;
            logMemo = I18nMessageUtil.getMessage(CustomerCommunicateConstant.PAYMENT_SHEET_RED_PREPAYMENT_ADD);
            pushTitle = I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_RED_PREPAYMENT_ADD_TITLE);
            pushContent = I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_RED_PREPAYMENT_ADD_CONTENT);
            xbbRefTypeEnum = XbbRefTypeEnum.RED_PREPAYMENT;
        } else {
            pushTypeEnum = PushTypeEnum.PAYMENT_SHEET_PREPAYMENT_ADD;
            logMemo = I18nMessageUtil.getMessage(CustomerCommunicateConstant.PAYMENT_SHEET_PREPAYMENT_ADD);
            pushTitle = I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_PREPAYMENT_ADD_TITLE);
            pushContent = I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_PREPAYMENT_ADD_CONTENT);
            xbbRefTypeEnum = XbbRefTypeEnum.PREPAYMENT;
        }
        if (!Objects.equals(DistributorMarkEnum.DISTRIBUTOR.getCode(), distributorMark)) {
            try {
                PaymentSheetDynamicStrategy paymentSheetDynamicStrategy = dynamicStrategyFactory.getPaymentSheetDynamicStrategyByBusinessType(businessType);
                final String finalUserId = userId;
                transactionHelp.afterCommitOrCommitNow(p->{
                    paymentSheetDynamicStrategy.prepayment(DynamicPaymentSheetDTO.initDynamicPaymentSheetDTO(paymentSheetEntityExt.getDataId(), paymentSheetEntityExt.getFormId(), saasFormSaveDTO,
                            userModel.getByKeyIngoreDel(finalUserId, corpid)));
                });
            } catch (Exception e) {
                LOG.error("com.xbongbong.saas.service.impl.PaymentSheetServiceImpl.prepaymentAfterSave", e);
            }
        }
        createLogAndMessage4Pre(xbbRefTypeEnum, pushTypeEnum, dataList, corpid, userId, paymentSheetEntityExt, logMemo, pushTitle, pushContent, distributorMark);
    }

    /**
     * 已核销、预收款核销afterSave方法
     *
     * @param saasFormSaveDTO
     */
    public void writeOffAfterSave(SaasFormSaveDTO saasFormSaveDTO) throws XbbException {
        Integer businessType = saasFormSaveDTO.getBusinessType();
        String corpid = saasFormSaveDTO.getCorpid();
        String userId = saasFormSaveDTO.getUserId();
        Integer distributorMark = saasFormSaveDTO.getDistributorMark();
        OnlinePaymentFlowEntity onlinePaymentFlowEntity = saasFormSaveDTO.getOnlinePaymentFlowEntity();
        PaasFormDataEntity newPaasFormDataEntity = saasFormSaveDTO.getNewPaasFormDataEntity();
        PaymentSheetEntityExt paymentSheetEntityExt = new PaymentSheetEntityExt();
        BeanUtil.copyProperties(newPaasFormDataEntity, paymentSheetEntityExt);
        JSONObject newData = newPaasFormDataEntity.getData();
        PaymentSheetTypeEnum paymentSheetTypeEnum = PaymentSheetTypeEnum.getByBusinessType(businessType);
        SheetTypeEnum sheetTypeEnum = paymentSheetTypeEnum.getSheetTypeEnum();
        SheetAttrPojo sheetAttrPojo = new SheetAttrPojo(XbbRefTypeEnum.PAYMENT_SHEET);
        Long sheetId = newPaasFormDataEntity.getDataId();
        //释放需要回滚信用流水
        boolean creditRollBack = false;
        //回款单的实收回款金额
        Double sheetAmount;
        double allAmount = 0D;
        boolean writeOffPrepaymentFlag = SheetTypeEnum.onlyWriteOffAmount(paymentSheetTypeEnum.getCode());
        Set<Long> contractIds = new HashSet<>();
        Set<Long> paymentIds = new HashSet<>();
        if (writeOffPrepaymentFlag) {
            //预收款核销，是用的核销金额
            sheetAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(newData, PaymentSheetEnum.WRITE_OFF_AMOUNT.getAttr(), 0D);
        } else {
            sheetAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(newData, PaymentSheetEnum.AMOUNT.getAttr(), 0D);
        }
        List<PaymentSheetEntityExt> sonSheetList = null;
        Map<String, FieldAttrEntity> explainMap = saasFormSaveDTO.getExplainMap();
        //应收款和合同对应的回款金额
        Map<Long, Double> contractIdToAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, Double> paymentIdToAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (Objects.nonNull(onlinePaymentFlowEntity)) {
            //在线支付回调成功的时候需要去更新在线支付流水时间
            onlinePaymentFlowEntity.getData().put(OnlinePaymentFlowDistributorEnum.SHEET_ID.getAttr(), Arrays.asList(newPaasFormDataEntity.getId()));
            onlinePaymentFlowEntity.getData().put(OnlinePaymentFlowDistributorEnum.SHEET_ID_LINK_TEXT.getAttr(), Arrays.asList(newPaasFormDataEntity.getSerialNo()));
            onlinePaymentFlowModel.update(onlinePaymentFlowEntity);
        }
        //处理回款单金额明细
        List<JSONObject> sonPaymentSheetDataList = saasSaveHelper.handleAmountDetail(saasFormSaveDTO, sheetAmount, sheetTypeEnum,
                sheetAttrPojo, newData, saasFormSaveDTO.getAmountDetail(), allAmount, contractIdToAmount, paymentIdToAmount, contractIds,
                paymentIds);
        //处理应收款信息
        HandleLinkInfoVO paymentLinkInfoVO = saasPaymentHelper.handlePayment(paymentSheetTypeEnum, newData, corpid, paymentIdToAmount, paymentIds);
        List<UpdateDataEntity> updatePayList = paymentLinkInfoVO.getUpdateList();
        //处理合同信息
        List<UpdateDataEntity> updateConList = contractHelp.handleContract(distributorMark, paymentSheetTypeEnum, corpid, contractIdToAmount, contractIds);
        //插入回款单子回款单
        sonSheetList = saasPaymentSheetHelper.savePaymentSheetSon(corpid, sonPaymentSheetDataList,  paymentSheetEntityExt, paymentSheetEntityExt.getUuid(), paymentLinkInfoVO);
        //更新应收款/合同
        paymentModel.savePaymentPlan(corpid, updatePayList);
        contractHelp.saveContract(corpid, updateConList, "PaymentSheetServiceImpl.normalSave：新建已核销回款单更新合同时失败");
        if (writeOffPrepaymentFlag) {
            //核销预收款回款单新建时，减少预收款余额
            saasPaymentSheetHelper.saveByPaymentSheet(paymentSheetEntityExt, distributorMark);
        }else {
            Long addTime = paymentSheetEntityExt.getAddTime();
            Long accountId = FastJsonHelper.getLongOrDefaultFromFormData(newData, PaymentSheetEnum.FUND_ACCOUNT.getAttr(), 0L);
            Long sheetTime = fundHelp.getSheetTime4FundFlow(newData, PaymentSheetEnum.PAYMENT_TIME.getAttr(), explainMap, addTime);
            //账户余额变动
            payAndPaymentSheetHelp.saveByPaymentSheetOrPaySheet(accountId, sheetAmount, corpid);
            //流水表日志记录
            FundAccountFlowEntity fundAccountFlowEntity = new FundAccountFlowEntity(corpid, addTime, paymentSheetEntityExt.getCreatorId(), FundAccountFlowTypeEnum.PAYMENT_SHEET.getCode(), sheetId, accountId, sheetTime, sheetAmount);
            fundAccountFlowModel.insert(fundAccountFlowEntity);
        }
        //按最小纬度进项生成客户对账单
        if (sonSheetList.size() > BasicConstant.ONE) {
            saasCustomerStatementHelper.save(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), explainMap, sonSheetList, distributorMark);
        } else {
            saasCustomerStatementHelper.save(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), explainMap, Arrays.asList(paymentSheetEntityExt), distributorMark);
        }
        //处理回款单团队-必须在团队保存后执行，需要用到回款单的id
        operateSheetTeam(paymentSheetEntityExt, sonSheetList, true, newPaasFormDataEntity, saasFormSaveDTO.getSaasNeedRedundantAttrPoJo(), saasFormSaveDTO.getIsImport(), false);
        //saasPaymentSheetHelper.handleSheetTeam(newData, corpid, sheetTypeEnum, paymentSheetEntityExt, sonSheetList, true);
        if (Objects.equals(distributorMark, DistributorMarkEnum.DISTRIBUTOR.getCode())) {
            Long distributorId = newData.getLong(PaymentSheetDistributorEnum.DISTRIBUTOR_ID.getAttr());
            String distributorName = newData.getString(PaymentSheetDistributorEnum.DISTRIBUTOR_NAME_LINK_TEXT.getAttr());
            if (!contractIds.isEmpty()) {
                if (Objects.equals(paymentSheetTypeEnum, PaymentSheetTypeEnum.WRITE_OFF)) {
                    // 回款单直接生成流水 > 该订货单有信用流水时，回款单用来还信用，即释放信用
                    List<Long> creditOrderIds = saasCreditHelper.filterCreditOrderIds(new ArrayList<>(contractIds), corpid);
                    if (creditOrderIds.size() > 0) {
                        creditRollBack = true;
                        Double creditOrderAmount = 0D;
                        for (Long creditOrderId : creditOrderIds) {
                            creditOrderAmount = Arith.add(creditOrderAmount, contractIdToAmount.getOrDefault(creditOrderId, 0D));
                        }
                        CreditCardBillInfoPojo infoPojo = new CreditCardBillInfoPojo(CreditCardBillTypeEnum.PAYMENT_SHEET.getCode(), sheetId, paymentSheetEntityExt.getSerialNo(), creditOrderAmount, distributorId, distributorName, XbbRefTypeEnum.PAYMENT_SHEET.getCode());
                        CreditCardBillDTO creditCardBillDTO = new CreditCardBillDTO(Arrays.asList(infoPojo));
                        BeanUtil.copyProperties(saasFormSaveDTO, creditCardBillDTO);
                        saasCreditHelper.addBatchCreditCardBill(creditCardBillDTO);
                    }
                } else if (Objects.equals(paymentSheetTypeEnum, PaymentSheetTypeEnum.WRITE_OFF_PREPAYMENT)) {
                    //预收款核销生成信用流水
                    BigDecimal bigDecimal = saasCreditHelper.calcCreditPayLimit(distributorId, new ArrayList<>(contractIds), new ArrayList<>(paymentIds), corpid);
                    if (bigDecimal.compareTo(BigDecimal.ZERO) > 0) {
                        creditRollBack = true;
                        CreditCardBillInfoPojo infoPojo = new CreditCardBillInfoPojo(CreditCardBillTypeEnum.PAYMENT_SHEET.getCode(), sheetId, paymentSheetEntityExt.getSerialNo(), bigDecimal.doubleValue(), distributorId, distributorName, XbbRefTypeEnum.PAYMENT_SHEET.getCode());
                        CreditCardBillDTO creditCardBillDTO = new CreditCardBillDTO(Arrays.asList(infoPojo));
                        BeanUtil.copyProperties(saasFormSaveDTO, creditCardBillDTO);
                        DistributorBaseInfoEntity distributorBaseInfoEntity = saasFormSaveDTO.getDistributorBaseInfoEntity();
                        if (Objects.nonNull(distributorBaseInfoEntity)) {
                            creditCardBillDTO.setUserId(distributorBaseInfoEntity.getDistributorUserId().toString());
                            creditCardBillDTO.setLoginUserName(distributorBaseInfoEntity.getDistributorUserName());
                            infoPojo.setCreatorIsDistributor(BasicConstant.ONE);
                        }
                        saasCreditHelper.addBatchCreditCardBill(creditCardBillDTO);
                    }
                }
            }
        }
        // 添加项目日志，及推送消息
        PaymentSheetSaveDTO paymentSheetSaveDTO = new PaymentSheetSaveDTO();
        BeanUtil.copyProperties(saasFormSaveDTO, paymentSheetSaveDTO);
        userId = saasSaveHelp.sheetAddLog(paymentSheetSaveDTO, corpid, userId, paymentSheetEntityExt, XbbRefTypeEnum.PAYMENT_SHEET.getName(), OperateModuleTypeEnum.PaymentManage);
        if (!Objects.equals(DistributorMarkEnum.DISTRIBUTOR.getCode(), saasFormSaveDTO.getDistributorMark())) {
            try {
                PaymentSheetDynamicStrategy paymentSheetDynamicStrategy = dynamicStrategyFactory.getPaymentSheetDynamicStrategyByBusinessType(businessType);
                final String finalUserId = userId;
                transactionHelp.afterCommitOrCommitNow(p->{
                    paymentSheetDynamicStrategy.writeOff(DynamicPaymentSheetDTO.initDynamicPaymentSheetDTO(paymentSheetEntityExt.getDataId(), paymentSheetEntityExt.getFormId(), saasFormSaveDTO,
                            userModel.getByKeyIngoreDel(finalUserId, corpid)));
                });
            } catch (Exception e) {
                LOG.error("com.xbongbong.saas.service.impl.PaymentSheetServiceImpl.writeOffAfterSave", e);
            }
        }
        createLogAndMessage(PushTypeEnum.PAYMENT_SHEET_ADD, newData, corpid, userId, paymentSheetEntityExt, I18nMessageUtil.getMessage(CustomerCommunicateConstant.PAYMENT_SHEET_ADD_CONTRACT), I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_ADD_TITLE), I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_ADD_CONTENT), distributorMark);
    }

    @Override
    public ListAppVO formatPaymentSheetAppList(FormDataListDTO formDataListDTO, List<PaasFormDataEntityExt> esEntities, Integer totalCount) throws XbbException {
        List<AppListPojo> appListPojos = new ArrayList<>();
        for (PaasFormDataEntityExt paymentSheet : esEntities) {
            JSONObject data = paymentSheet.getData();
            Long paymentSheetId = paymentSheet.getId();
            Long appId = paymentSheet.getAppId();
            Long menuId = paymentSheet.getMenuId();
            Long formId = paymentSheet.getFormId();
            List<FormTitlePoJo> titleList = FastJsonHelper.getJsonArrOrDefaultFromFormData(data, BasicConstant.TITLE_LIST, new JSONArray()).toJavaList(FormTitlePoJo.class);
            List<SummaryDataPoJo> summaryList = FastJsonHelper.getJsonArrOrDefaultFromFormData(data, BasicConstant.SUMMARY_LIST, new JSONArray()).toJavaList(SummaryDataPoJo.class);
            List<SummaryDataPoJo> lablesList = FastJsonHelper.getJsonArrOrDefaultFromFormData(data, BasicConstant.LABELS_LIST, new JSONArray()).toJavaList(SummaryDataPoJo.class);
            JSONObject others = disposePaymentSheetOthers(data);
            JSONObject superScript = new JSONObject();
            //app系统关联数据展示需要 others 和 superScript
            data.put("others",others);
            data.put("superScript",superScript);

            appListPojos.add(new AppListPojo(paymentSheetId, titleList, summaryList, superScript, lablesList, others, appId, menuId, formId));
        }
        ListAppVO listAppVO = new ListAppVO();
        listAppVO.setList(appListPojos);
        listAppVO.setTotalCount(totalCount);
        return listAppVO;
    }

    @Override
    public PaymentSheetUpdateBatchVO updateAttrBatch(PaymentSheetUpdateBatchDTO paymentSheetUpdateBatchDTO) throws XbbException {
        PaymentSheetUpdateBatchVO paymentSheetUpdateBatchVO = new PaymentSheetUpdateBatchVO();
        try {
            String corpid = paymentSheetUpdateBatchDTO.getCorpid();
            List<PaymentSheetUpdateDTO> paymentSheetList = paymentSheetUpdateBatchDTO.getPaymentSheetUpdateList();
            if (Objects.nonNull(paymentSheetList)
                    && !paymentSheetList.isEmpty()) {
                List<UpdateDataEntity> list = new ArrayList<>();
                paymentSheetList.forEach((item) -> {
                    list.add(ExplainUtil.getUpdateData(item.getId(), item.getData(), corpid));
                });
                if (!list.isEmpty()) {
                    paymentSheetModel.updateBatch(list, corpid);
                }
            }
        } catch (Exception e) {
            LOG.error("PaymentSheetServiceImpl.updateBatch 批量更新出错：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return paymentSheetUpdateBatchVO;
    }

    @Override
    public PaymentSheetUpdateBatchVO updateAttrBatch(PaymentSheetUpdateBatchDTO paymentSheetUpdateBatchDTO, boolean needThrow) throws XbbException {
        PaymentSheetUpdateBatchVO paymentSheetUpdateBatchVO = new PaymentSheetUpdateBatchVO();
        try {
            String corpid = paymentSheetUpdateBatchDTO.getCorpid();
            List<PaymentSheetUpdateDTO> paymentSheetList = paymentSheetUpdateBatchDTO.getPaymentSheetUpdateList();
            if (Objects.nonNull(paymentSheetList)
                    && !paymentSheetList.isEmpty()) {
                List<UpdateDataEntity> list = new ArrayList<>();
                paymentSheetList.forEach((item) -> {
                    list.add(ExplainUtil.getUpdateData(item.getId(), item.getData(), corpid));
                });
                if (!list.isEmpty()) {
                    paymentSheetModel.updateBatch(list, corpid, needThrow);
                }
            }
        } catch (Exception e) {
            LOG.error("PaymentSheetServiceImpl.updateBatch 批量更新出错：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return paymentSheetUpdateBatchVO;
    }

    @Override
    public void batchEditAttr(PaymentSheetEditAttrUpdateDTO paymentSheetEditAttrUpdateDTO) throws XbbException {
        try {
            List<PaymentSheetUpdateDTO> paymentSheetUpdateList =new ArrayList<>();
            List<Long> dataIdList = paymentSheetEditAttrUpdateDTO.getDataIdList();
            //预收款核销无自定义字段，需要排除
            String corpid = paymentSheetEditAttrUpdateDTO.getCorpid();
            List<String> fieldList = Arrays.asList("id", "flowStatus", PaymentSheetEnum.SHEET_NO.getAttr(), PaymentSheetEnum.getAttrConnectData(PaymentSheetEnum.PAYMENT_SHEET_TYPE), FieldTypeEnum.SHEET_ALONE.getAlias());
            List<PaasFormDataEntityExt> paymentSheetEntityList = formHelp.getFormListByIds4All(corpid, IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET, dataIdList, fieldList);
            if (Objects.isNull(paymentSheetEntityList)){
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
            }
            //权限团队校验前置了
            List<Long> errorIdList = new ArrayList<>();
//            List<Long> errorIdList = userTeamHelp.batchTeamMembersCheckEditPermission(paymentSheetEditAttrUpdateDTO.getBusinessType(), paymentSheetEditAttrUpdateDTO.getSaasMark(),
//                    paymentSheetEditAttrUpdateDTO.getMenuId(), paymentSheetEditAttrUpdateDTO.getLoginUser(), paymentSheetEditAttrUpdateDTO.getDistributorMark(),
//                    paymentSheetEditAttrUpdateDTO.getDataIdList(), new HashMap<>());
            List<String> errorNameList = new ArrayList<>();
            for (PaasFormDataEntityExt paasFormDataEntityExt : paymentSheetEntityList) {
                if (errorIdList.contains(paasFormDataEntityExt.getId())) {
                    errorNameList.add(paasFormDataEntityExt.getSerialNo());
                }
            }
            String nameStr = org.apache.commons.lang.StringUtils.join(errorNameList, "、");
            if (CollectionUtils.isNotEmpty(errorNameList)) {
                throw new XbbException(UserTeamErrorCodeEnum.API_ERROR_271038.getCode(), String.format(UserTeamErrorCodeEnum.API_ERROR_271038.getMsg(), nameStr));
            }
            List<String> allAttr;
            if (Objects.equals(paymentSheetEditAttrUpdateDTO.getDistributorMark(), DistributorMarkEnum.DISTRIBUTOR.getCode())) {
                allAttr = PaymentSheetDistributorEnum.getAllAttr();
            } else {
                allAttr = PaymentSheetEnum.getAllAttr();
            }

            Boolean isFieldDependence = paymentSheetEditAttrUpdateDTO.getIsFieldDependence();
            AttrValuePojo singleAttrValuePojo = ProSaveHelp.getSingleAttrValuePojo(isFieldDependence, paymentSheetEditAttrUpdateDTO.getFieldEditedList());
            String attr = singleAttrValuePojo.getAttr();
            //true扩展的自定义字段（PaymentSheetEnum不包含的字段），false非自定义字段（PaymentSheetEnum内包含的字段）
            // 注：字段依赖的四个字段，是PaySheetEnum不包含的字段
            boolean redundantFlag = !allAttr.contains(attr);
            // 批量编辑日志
            List<String> updataItemList = new ArrayList<>(paymentSheetEntityList.size());
            List<Long> idList = new ArrayList<>(paymentSheetEntityList.size());
            for (PaasFormDataEntityExt entityExt : paymentSheetEntityList) {
                Long id = entityExt.getId();
                JSONObject data = entityExt.getData();
                if (data == null) {
                    continue;
                }
                String type = data.getString(PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr());
                //预收款核销无自定义字段，批量编辑时需要跳过自定义字段的批量设置
                if (Objects.equals(type, PaymentSheetTypeEnum.WRITE_OFF_PREPAYMENT.getCode()) && redundantFlag) {
                    continue;
                }
                //回款单批量编辑支付方式排除红冲预收款余额和合并回款
                Integer alone = entityExt.getAlone();
                boolean ifEditPayMethod = (Objects.equals(PaymentSheetTypeEnum.RED_PREPAYMENT.getCode(), type) || Objects.equals(SheetAloneEnum.PARENT.getCode(), alone))
                        && Objects.equals(PaymentSheetEnum.PAY_METHOD.getAttr(), attr);
                if (ifEditPayMethod) {
                    continue;
                }
                idList.add(id);
                updataItemList.add(entityExt.getSerialNo());
                PaymentSheetUpdateDTO payment = new PaymentSheetUpdateDTO();
                payment.setId(id);
                JSONObject updateData = ProSaveHelp.getBatchUpdateData(paymentSheetEditAttrUpdateDTO.getFieldEditedList());
                payment.setData(updateData);
                paymentSheetUpdateList.add(payment);
            }
            if (!paymentSheetUpdateList.isEmpty()) {
                PaymentSheetUpdateBatchDTO paymentSheetUpdateBatchDTO = new PaymentSheetUpdateBatchDTO();
                BeanUtil.copyProperties(paymentSheetEditAttrUpdateDTO, paymentSheetUpdateBatchDTO);
                paymentSheetUpdateBatchDTO.setPaymentSheetUpdateList(paymentSheetUpdateList);
                updateAttrBatch(paymentSheetUpdateBatchDTO);

                String updataItem = StringUtils.join(updataItemList, "，");
                String userId = paymentSheetEditAttrUpdateDTO.getUserId();
                String loginUserName = paymentSheetEditAttrUpdateDTO.getLoginUserName();
                // 标签特殊处理
                Integer fieldType = paymentSheetEditAttrUpdateDTO.getFieldType();
                logHelp.analysisLabel(corpid, fieldType, isFieldDependence, paymentSheetEditAttrUpdateDTO.getLogFieldEditedList());

                String changedLogItemsStr = logHandleHelp.getChangedLogItemsStr(paymentSheetEditAttrUpdateDTO.getLogFieldEditedList());
                if(idList.size() > 1 ){
                    String memo = format(I18nMessageUtil.getMessage(LogMemoConstant.LOG_MEMO_USER_UPDATE_FORM_DATA_BATCH_ATTR), loginUserName, XbbRefTypeEnum.PAYMENT_SHEET.getName(), changedLogItemsStr, idList.size());
                    List<InfoArrPojo> infoArrPojoList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    InfoArrPojo infoArrPojo = new InfoArrPojo();
                    infoArrPojo.setTitle(XbbRefTypeEnum.PAYMENT_SHEET.getName());
                    infoArrPojo.setContent(updataItem);
                    infoArrPojoList.add(infoArrPojo);
                    mongoLogHelp.buildLog4InfoArr(infoArrPojoList, corpid, userId, loginUserName, OperateModuleTypeEnum.PaymentManage, OperateTypeEnum.EDIT, "", "", memo, paymentSheetEditAttrUpdateDTO.getHttpHeader());
                } else if (Objects.equals(idList.size(), 1)){
                    String memo = format(I18nMessageUtil.getMessage(LogMemoConstant.LOG_MEMO_USER_UPDATE_FORM_DATA_ATTR), loginUserName, XbbRefTypeEnum.PAYMENT_SHEET.getName(), updataItem, changedLogItemsStr);
                    mongoLogHelp.buildLog(corpid, userId, loginUserName, OperateModuleTypeEnum.PaymentManage, OperateTypeEnum.EDIT, idList.get(0).toString(), updataItem, memo, paymentSheetEditAttrUpdateDTO.getHttpHeader());
                }
            }
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("PaymentSheetServiceImpl.batchEditAttr 执行出错：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
    }

    /**
     * 处理others字段
     *
     * @param data paymentSheet数据
     * @return com.alibaba.fastjson.JSONObject
     * @throws XbbException 异常
     * @author 徐俊杰
     * @date 2019/11/21 9:29
     * @since v1.0
     */
    private JSONObject disposePaymentSheetOthers(JSONObject data) throws XbbException {
        JSONObject others = new JSONObject();
        //内部展示的是文字
        String status = commonHelp.formatFundStatusText(data,PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr(),PaymentSheetTypeEnum.UNKNOWN.getName());
        //文字转为code
        String type = PaymentSheetTypeEnum.getByName(status).getCode();
        String amount = StringUtil.formatDouble(FundHelp.getSheetAmount4PaymentSheet(data, type)) + UnitEnum.MONEY.getName();
        others.put("amount", amount);
        others.put("status", status);
        //处理红冲和坏账状态
        boolean red = PaymentSheetTypeEnum.isRed(type);
        int redStatus = red ? 1 : 0;
        boolean bad = PaymentSheetTypeEnum.isBad(type);
        int badStatus = bad ? 1 : 0;
        others.put("isRed", redStatus);
        others.put("isBad", badStatus);
        return others;
    }



    /**
     * 批量保存前置
     *
     * @param formDataSaveDTO
     * @param paymentSheetTypeEnum
     * @param customerId
     * @return
     * @throws XbbException
     * @author chenshan
     * @date 2019-11-27 11:08
     */
    @Override
    public AddBatchDTO beforeSaveBatch(FormDataAddDTO formDataSaveDTO, PaymentSheetTypeEnum paymentSheetTypeEnum, Long customerId) throws XbbException {
        PaasFormExplainEntity paasFormExplainEntity = paasFormExplainModel.getByFormId(formDataSaveDTO.getFormId(), formDataSaveDTO.getCorpid());
        if (Objects.isNull(paasFormExplainEntity)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_EXPLAIN_NOT_EXIST);
        }
        Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(paasFormExplainEntity.getExplains(), null);
        JSONObject data = formDataSaveDTO.getDataList();
        saasSaveHelp.formatLinkBusiness4Save(data, PaymentSheetEnum.CUSTOMER_ID.getAttr(), PaymentSheetEnum.CUSTOMER_NAME_LINK_TEXT.getAttr(), PaymentSheetEnum.CUSTOMER_ID.getFieldType());
        saasSaveHelp.formatLinkBusiness4Save(data, PaymentSheetEnum.CONTRACT.getAttr(), PaymentSheetEnum.CONTRACT_LINK_TEXT.getAttr(), PaymentSheetEnum.CONTRACT.getFieldType());
        Double contractAmount;
        SaasNeedRedundantAttrPojo saasNeedRedundantAttrPoJo = formDataSaveDTO.getSaasNeedRedundantAttrPoJo();
        if (Objects.nonNull(saasNeedRedundantAttrPoJo) && Objects.equals(saasNeedRedundantAttrPoJo.getParentBusinessType(), XbbRefTypeEnum.CONTRACT.getCode())) {
            contractAmount = formDataSaveDTO.getSaasNeedRedundantAttrPoJo().getParentAmount();
        } else {
            JSONArray idArray = data.getJSONArray(PaymentSheetEnum.CONTRACT.getAttr());
            Long dataId = 0L;
            if ( CollectionsUtil.isNotEmpty(idArray) ) {
                dataId = idArray.getLong(0);
            }
            PaasFormDataEntityExt contractEntityExt = contractModel.getByKey(dataId,formDataSaveDTO.getCorpid());
            if (Objects.isNull(contractEntityExt)) {
                throw new XbbException(ContractErrorCodeEnum.API_ERROR_203001);
            }
            contractAmount = contractEntityExt.getData().getDouble(ContractEnum.AMOUNT.getAttr());
        }
        Double paymentSheetAmount = 0D;
        Double advancePaymentBalance = 0D;
        JSONArray paymentSheetArray = data.getJSONArray("addPaymentSheet");
        String xbbRefTypeName = XbbRefTypeEnum.PAYMENT_SHEET.getName();
        if ( Objects.equals(PaymentSheetTypeEnum.WRITE_OFF_PREPAYMENT.getCode(), paymentSheetTypeEnum.getCode()) ){
            paymentSheetArray = data.getJSONArray("addWriteOffPaymentSheet");
            xbbRefTypeName = XbbRefTypeEnum.WRITE_OFF_PREPAYMENT.getName();
        }

        List<AddMuchFieldPojo> addBatchList = new ArrayList<>();
        List<AddMuchFieldPojo> delBatchList = new ArrayList<>();
        List<AddMuchFieldPojo> updateBatchList = new ArrayList<>();
        List<ValidateDataDTO> validateDataDTOList = new ArrayList<>();
        FieldAttrEntity serialNoFieldAttr = explainMap.get(PaymentSheetEnum.SHEET_NO.getAttr());
        //下面是移除审批中已经删除的回款单
        paasFormExplainService.handProcessShouldRemoveValue(formDataSaveDTO.getCorpid(), XbbRefTypeEnum.CONTRACT, XbbRefTypeEnum.PAYMENT_SHEET, paymentSheetArray);
        for (Object obj : paymentSheetArray){
            AddMuchFieldPojo addMuchFieldPojo = new AddMuchFieldPojo();
            ValidateDataDTO validateDataDTO = new ValidateDataDTO();
            JSONObject json = (JSONObject) obj;
            Long dataId = json.getLong(StringConstant.DATA_ID);
            addMuchFieldPojo.setId(dataId);
            String serialNo = json.getString(FieldTypeEnum.SERIALNO.getAlias());
            if (Objects.nonNull(serialNoFieldAttr)) {
                if (Objects.equals(serialNoFieldAttr.getStrictController(), BasicConstant.ONE) && StringUtil.isEmpty(serialNo)) {
                    serialNo = saasSaveHelp.getSerialNo(serialNo, formDataSaveDTO, formDataSaveDTO.getCorpid(), serialNoFieldAttr);
                }
                addMuchFieldPojo.setSerialNo(serialNo);
            }
            if ( StringUtil.isNotEmpty(serialNo) && serialNo.length()>BasicConstant.SERIAL_LENGTH_NUMBER) {
                throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208059,String.format(PaymentErrorCodeEnum.API_ERROR_208059.getMsg(),xbbRefTypeName,BasicConstant.SERIAL_LENGTH_NUMBER));
            }
            JSONObject paymentSheetData = json.getJSONObject("data");
            if (Objects.isNull(paymentSheetData)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, SystemErrorCodeEnum.API_ERROR_100028.getMsg());
            }
            Integer alone = json.getInteger(BasicConstant.ALONE);
            boolean flag = Objects.isNull(dataId) && (Objects.isNull(alone) || Objects.equals(alone, SheetAloneEnum.NORMAL.getCode()));
            if (flag) {
                JSONArray belongIdArray = paymentSheetData.getJSONArray(PaymentSheetEnum.BELONG_ID.getAttr());
                if ( belongIdArray.size() > 1 ){
                    throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208047, PaymentErrorCodeEnum.API_ERROR_208047.getMsg(), paymentSheetTypeEnum.getName());
                }
            }
            for (Map.Entry<String,Object> entry : SaasDataInitHelp.initPaymentSheet().entrySet()){
                SaasDataInitHelp.antDataSet(paymentSheetData,entry.getKey(),entry.getValue());
            }
            if ( Objects.equals(PaymentSheetTypeEnum.WRITE_OFF.getCode(), paymentSheetTypeEnum.getCode()) ){
                JSONObject jsonObject = FastJsonHelper.getJsonObjectFromFormData(paymentSheetData, PaymentSheetEnum.FUND_ACCOUNT.getAttr());
                if (Objects.nonNull(jsonObject)) {
                    paymentSheetData.put(PaymentSheetEnum.FUND_ACCOUNT.getAttr(),jsonObject.get(StringConstant.VALUE));
                }
                paymentSheetData.put(PaymentSheetEnum.WRITE_OFF_AMOUNT.getAttr(), getDoubleOrDefaultFromFormData(paymentSheetData, PaymentSheetEnum.AMOUNT.getAttr(), 0D));
            }
            if ( Objects.isNull(dataId)) {
                paymentSheetData.put(PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr(), paymentSheetTypeEnum.getCode());
            }
            paymentSheetData.put(PaymentSheetEnum.CUSTOMER_ID.getAttr(),data.get(PaymentSheetEnum.CUSTOMER_ID.getAttr()));
            paymentSheetData.put(PaymentSheetEnum.CUSTOMER_NAME_LINK_TEXT.getAttr(),data.get(PaymentSheetEnum.CUSTOMER_NAME_LINK_TEXT.getAttr()));
            paymentSheetData.put(PaymentSheetEnum.CONTRACT.getAttr(),data.get(PaymentSheetEnum.CONTRACT.getAttr()));
            paymentSheetData.put(PaymentSheetEnum.CONTRACT_LINK_TEXT.getAttr(),data.get(PaymentSheetEnum.CONTRACT_LINK_TEXT.getAttr()));
            Integer operationFlag = json.getInteger(StringConstant.OPERATION_FLAG);
            addMuchFieldPojo.setData(paymentSheetData);
            if ( Objects.isNull(dataId) ) {
                operationFlag = null;
            }
            BeanUtil.copyProperties(formDataSaveDTO,validateDataDTO);
            validateDataDTO.setSerialNo(serialNo);
            validateDataDTO.setBusinessType(XbbRefTypeEnum.PAYMENT_SHEET.getCode());
            validateDataDTO.setData(addMuchFieldPojo.getData());
            validateDataDTO.setDataId(dataId);
            if (Objects.equals(operationFlag, LinkAddOperationFlagEnum.DEL.getCode())){
                delBatchList.add(addMuchFieldPojo);
            }else {
                if (Objects.equals(operationFlag,LinkAddOperationFlagEnum.EDIT.getCode())){
                    updateBatchList.add(addMuchFieldPojo);
                    validateDataDTOList.add(validateDataDTO);
                }else if (Objects.isNull(dataId)){
                    //为了防止进入审批，前置传入
                    addMuchFieldPojo.setAlone(SheetAloneEnum.NORMAL.getCode());
                    addMuchFieldPojo.setUuid(UUID.randomUUID().toString());
                    addBatchList.add(addMuchFieldPojo);
                    validateDataDTOList.add(validateDataDTO);
                    if ( Objects.equals(PaymentSheetTypeEnum.WRITE_OFF_PREPAYMENT.getCode(), paymentSheetTypeEnum.getCode()) ) {
                        advancePaymentBalance = Arith.add(advancePaymentBalance, getDoubleOrDefaultFromFormData(paymentSheetData, PaymentSheetEnum.WRITE_OFF_AMOUNT.getAttr(), 0D));
                    }
                }
                if ( Objects.equals(PaymentSheetTypeEnum.WRITE_OFF_PREPAYMENT.getCode(), paymentSheetTypeEnum.getCode())  ){
                    paymentSheetAmount = Arith.add(paymentSheetAmount, getDoubleOrDefaultFromFormData(paymentSheetData, PaymentSheetEnum.WRITE_OFF_AMOUNT.getAttr(), 0D));
                } else {
                    paymentSheetAmount = Arith.add(paymentSheetAmount, getDoubleOrDefaultFromFormData(paymentSheetData, PaymentSheetEnum.AMOUNT.getAttr(), 0D));
                }
            }
            validateDataDTO.setExplainMap(explainMap);
            // 标签设置
            saasSaveHelp.dealLabelData(validateDataDTO.getData(), validateDataDTO.getExplainMap());
            formDataValidateDataHelp.validateData(validateDataDTO);
            ProSaveHelp.analyticalData(validateDataDTO);
            addMuchFieldPojo.setDepartmentId(validateDataDTO.getDepartmentId());
            paymentSheetData.put(FieldTypeEnum.DEPARTMENTID.getAlias(), validateDataDTO.getDepartmentId());
        }
        if ( Objects.equals(PaymentSheetTypeEnum.WRITE_OFF_PREPAYMENT.getCode(), paymentSheetTypeEnum.getCode()) && advancePaymentBalance > 0 ) {
            PaymentBalanceEntity paymentBalanceEntity = paymentBalanceModel.getByCustomerId(customerId, formDataSaveDTO.getCorpid());
            if ( paymentBalanceEntity == null || paymentBalanceEntity.getAdvancePaymentBalance() < advancePaymentBalance ) {
                throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208036, PaymentErrorCodeEnum.API_ERROR_208036.getMsg());
            }
        }
        //允许误差小于等于ConfigConstant.amountAccuracy
        if (Arith.sub(paymentSheetAmount, contractAmount) > ConfigConstant.amountAccuracy) {
            throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208046, PaymentErrorCodeEnum.API_ERROR_208046.getMsg(), paymentSheetTypeEnum.getName());
        }
        AddBatchDTO addBatchDTO = new AddBatchDTO();
        BeanUtil.copyProperties(formDataSaveDTO,addBatchDTO);
        addBatchDTO.setAddBatchList(addBatchList);
        FieldAttrEntity serialField = explainMap.getOrDefault(PaymentSheetEnum.SHEET_NO.getAttr(), new FieldAttrEntity());
        if (Objects.equals(serialField.getNoRepeat(), 1)) {
            Set<String> addSerialNoSet = new HashSet<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            addBatchList.forEach(addBatch-> addSerialNoSet.add(addBatch.getSerialNo()));
            if (!Objects.equals(addSerialNoSet.size(), addBatchList.size())) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.NO_REPEAT, serialField.getAttrName());
            }
        }
        Integer total = addBatchList.size() + updateBatchList.size() - delBatchList.size();
        if ( Objects.equals(paymentSheetTypeEnum.getCode(), PaymentSheetTypeEnum.WRITE_OFF_PREPAYMENT.getCode()) ){
            if ( /*formDataSaveDTO.getIsNew() &&*/ total > StringConstant.MAX_WRITE_OFF_PREPAYMENT_COUNT ) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.OVER_STEP_MAX_WRITE_OFF_PREPAYMENT_COUNT, StringConstant.MAX_WRITE_OFF_PREPAYMENT_COUNT);
            }
        } else if (Objects.equals(paymentSheetTypeEnum.getCode(), PaymentSheetTypeEnum.WRITE_OFF.getCode())){
            if ( /*formDataSaveDTO.getIsNew() &&*/ total > StringConstant.MAX_PAYMENT_SHEET_COUNT ) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.OVER_STEP_MAX_PAYMENT_SHEET_COUNT, StringConstant.MAX_PAYMENT_SHEET_COUNT);
            }
        }
        addBatchDTO.setAmount(paymentSheetAmount);
        addBatchDTO.setUpdateBatchList(updateBatchList);
        addBatchDTO.setDelBatchList(delBatchList);
        addBatchDTO.setValidateDataDTOList(validateDataDTOList);
        return addBatchDTO;
    }

    /**
     * 批量保存回款单
     *
     * @param paymentSheetDTO
     * @return
     * @throws XbbException
     */
    @Override
    public PaymentSheetAmountVO addBatch(PaymentSheetDTO paymentSheetDTO, PaymentSheetTypeEnum paymentSheetTypeEnum, Long customerId) throws XbbException {
        String loginUserName = paymentSheetDTO.getLoginUserName();
        String corpid = paymentSheetDTO.getCorpid();
        String userId = paymentSheetDTO.getUserId();
        // 审批兼容
        if (paymentSheetDTO.getProcessCreatorId() != null) {
            userId = paymentSheetDTO.getProcessCreatorId();
            UserEntity userEntity = userModel.getByKeyIngoreDel(userId, corpid);
            loginUserName = userEntity.getName();
        }

        PaymentSheetAmountVO paymentSheetAmountVO = new PaymentSheetAmountVO();
        // 批量新增流水号集合
        List<AddMuchFieldPojo> addBatchList = paymentSheetDTO.getAddBatchList();
        List<String> addBatchSerialNoList= new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        // 开启严控模式后生成流水号
        PaasFormExplainEntity formExplainEntity = paasFormExplainModel.getByFormId(paymentSheetDTO.getFormId(), paymentSheetDTO.getCorpid());
        if (Objects.isNull(formExplainEntity)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_EXPLAIN_NOT_EXIST);
        }
        Integer distributorMark = formExplainEntity.getDistributorMark();
        paymentSheetDTO.setDistributorMark(distributorMark);
        boolean distributorMarkFlag = Objects.equals(distributorMark, DistributorMarkEnum.DISTRIBUTOR.getCode());
        Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(formExplainEntity.getExplains(), null);
        FieldAttrEntity fieldAttrEntity = explainMap.get(PaymentEnum.PAYMENT_NO.getAttr());
        if (Objects.nonNull(fieldAttrEntity)) {
            FormDataAddDTO formDataAddDTO = new FormDataAddDTO();
            BeanUtil.copyProperties(paymentSheetDTO, formDataAddDTO);
            for (AddMuchFieldPojo addMuchFieldPojo : addBatchList) {
                if (Objects.equals(fieldAttrEntity.getStrictController(), BasicConstant.ONE) &&  StringUtil.isEmpty(addMuchFieldPojo.getSerialNo())) {
                    String serialNo = "";
                    serialNo = saasSaveHelp.getSerialNo(serialNo, formDataAddDTO, corpid, fieldAttrEntity);
                    addMuchFieldPojo.setSerialNo(serialNo);
                }
                addBatchSerialNoList.add(addMuchFieldPojo.getSerialNo());
            }
        }
        //获取一下回款单当前处于什么模式
        Long modelType = fundSetService.getModelType(corpid, XbbRefTypeEnum.CRM);
        addBatchList.forEach(addMuchFieldPojo -> {
            addMuchFieldPojo.setSysLong10(modelType);
        });
        String addBatchSerialNoStr = StringUtils.join(addBatchSerialNoList, "，");
        // 批量更新流水号集合
        List<AddMuchFieldPojo> updateBatchList = paymentSheetDTO.getUpdateBatchList();
        // 批量删除流水号集合
        List<AddMuchFieldPojo> delBatchList = paymentSheetDTO.getDelBatchList();

        try {
            PaymentSheetInsertBatchByContractVO paymentSheetInsertBatchByContractVO = null;
            PaasFormEntityExt paasFormEntityExt = paasFormModel.getByBusinessType4Distributor(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), corpid, distributorMark, null);
            // 删除逻辑
            if (Objects.nonNull(delBatchList) && !delBatchList.isEmpty()) {
                List<Long> idList = new ArrayList<>();
                for (AddMuchFieldPojo item : delBatchList) {
                    idList.add(item.getId());
                }
                PaymentSheetDeleteBatchDTO paymentSheetDeleteBatchDTO = new PaymentSheetDeleteBatchDTO();
                BeanUtil.copyProperties(paymentSheetDTO, paymentSheetDeleteBatchDTO);
                paymentSheetDeleteBatchDTO.setIdIn(idList);
                PaymentSheetDeleteBatchVO paymentSheetDeleteBatchVO = deleteBatch(paymentSheetDeleteBatchDTO, DelEnum.DELETE);
                if(Objects.isNull(paymentSheetDeleteBatchVO)) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
                }
                if (CollectionsUtil.isNotEmpty(idList)) {
                    List<UserTeamEntity> userTeamEntities = userTeamHelp.getUserTeamList(idList, corpid, XbbRefTypeEnum.PAYMENT_SHEET.getCode(), false, null);
                    if (CollectionsUtil.isNotEmpty(userTeamEntities)) {
                        List<Long> userIdIn = userTeamEntities.stream().map(UserTeamEntity::getId).collect(Collectors.toList());
                        paymentSheetUserModel.batchUpdateByUserIdIn(corpid, userIdIn, userTeamEntities, null, DelEnum.DELETE.getDel(),false);
                    }
                }
            }

            //非经销商的，保持原逻辑，先进审批再入主表
            if(/*!distributorMarkFlag && */!Objects.equals(paymentSheetDTO.getIsImport(), 1) && Objects.nonNull(paasFormEntityExt) && Objects.equals(paasFormEntityExt.getIsProcessForm(), 1)){
                Iterator<AddMuchFieldPojo> iterator = addBatchList.iterator();
                while (iterator.hasNext()) {
                    AddMuchFieldPojo addMuchFieldPojo = iterator.next();
                    StartProcessDTO startProcessDTO = new StartProcessDTO();
                    BeanUtil.copyProperties(paymentSheetDTO, startProcessDTO, true);
                    BeanUtil.copyProperties(addMuchFieldPojo, startProcessDTO, true);
                    startProcessDTO.setFormId(paasFormEntityExt.getId());
                    startProcessDTO.setFormDataId(0L);
                    startProcessDTO.setSaasMark(SaasMarkEnum.SAAS.getCode());
                    startProcessDTO.setBusinessType(XbbRefTypeEnum.PAYMENT_SHEET.getCode());
                    if (paymentSheetDTO.getProcessCreatorId() == null) {
                        startProcessDTO.setCreatorId(paymentSheetDTO.getUserId());
                    } else {
                        startProcessDTO.setCreatorId(paymentSheetDTO.getProcessCreatorId());
                    }
                    JSONObject data = addMuchFieldPojo.getData();
                    //厂商端新建经销商类型回款单，且为已核销类型，则支付方式默认置为代客操作
                    String sheetType = FastJsonHelper.getStringOrDefaultFromFormData(data, PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr(), "");
                    saasSaveHelp.handlePayMethod4Distributor(paymentSheetDTO.getPlatform(), data, sheetType, distributorMark);
                    startProcessDTO.setData(data);
//                    if (distributorMarkFlag) {
//                        if (postPositionProcessHelp.isAutoReject(startProcessDTO)) {
//                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.START_PROCESS_ERROR);
//                        }
//                        continue;
//                    }
                    StartProcessVO startProcessVO = processStartHelp.startProcess(startProcessDTO, null);
                    if (Objects.isNull(startProcessVO)) {
                        throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.START_PROCESS_ERROR);
                    }
                    Integer noApprovalRequired = startProcessVO.getNoApprovalRequired();
                    if ( !Objects.equals(noApprovalRequired, 1) ){
                        iterator.remove();
                    }
                }
            }

            // 插入数据
            if(Objects.nonNull(addBatchList) && !addBatchList.isEmpty()){
                PaymentSheetInsertBatchByContractDTO paymentSheetInsertBatchByContractDTO = new PaymentSheetInsertBatchByContractDTO();
                BeanUtil.copyProperties(paymentSheetDTO, paymentSheetInsertBatchByContractDTO);
                paymentSheetInsertBatchByContractDTO.setDataList(paymentSheetDTO.getAddBatchList());
                paymentSheetInsertBatchByContractVO = insertBatch(paymentSheetTypeEnum,paymentSheetInsertBatchByContractDTO, paymentSheetDTO.getParentNew(),userId, customerId);
                paymentSheetAmountVO.setRealPaymentTotalAmount(paymentSheetInsertBatchByContractVO.getRealPaymentTotalAmount());
                // 记录日志
                List<PaymentSheetEntityExt> insertList = paymentSheetInsertBatchByContractVO.getInsertList();
                if(insertList.size() > 1){
                    String memo = format(I18nMessageUtil.getMessage(LogMemoConstant.LOG_MEMO_USER_BATCH_ADD_FORM_DETAIL), loginUserName, paymentSheetTypeEnum.getName(), insertList.size());
                    List<InfoArrPojo> infoArrPojoList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    InfoArrPojo infoArrPojo = new InfoArrPojo();
                    infoArrPojo.setTitle(PaymentSheetEnum.SHEET_NO.getAttrName());
                    infoArrPojo.setContent(addBatchSerialNoStr);
                    infoArrPojoList.add(infoArrPojo);
                    mongoLogHelp.buildLog4InfoArr(infoArrPojoList, corpid, userId, loginUserName, OperateModuleTypeEnum.PaymentManage, OperateTypeEnum.NEW,
                            "", "", memo, paymentSheetDTO.getHttpHeader());
                } else if (Objects.equals(insertList.size(), 1)){
                    String memo = format(I18nMessageUtil.getMessage(LogMemoConstant.LOG_MEMO_USER_ADD_WORK), loginUserName, XbbRefTypeEnum.PAYMENT_SHEET.getName(), addBatchSerialNoStr);
                    mongoLogHelp.buildLog(corpid, userId, loginUserName, OperateModuleTypeEnum.PaymentManage, OperateTypeEnum.NEW,
                            insertList.get(0).getId().toString(), addBatchSerialNoStr, memo, paymentSheetDTO.getHttpHeader());
                }
                /*if (distributorMarkFlag) {
                    //经销商的，后置执行进入审批
                    Iterator<PaymentSheetEntityExt> iterator = insertList.iterator();
                    while (iterator.hasNext()) {
                        PaasFormDataEntity newPaasFormDataEntity = iterator.next();
                        StartProcessDTO startProcessDTO = new StartProcessDTO();
                        BeanUtil.copyProperties(paymentSheetDTO, startProcessDTO, true);
                        BeanUtil.copyProperties(newPaasFormDataEntity, startProcessDTO, true);
                        startProcessDTO.setFormDataId(newPaasFormDataEntity.getId());
                        startProcessDTO.setDistributorMark(distributorMark);
                        startProcessDTO.setSaasMark(SaasMarkEnum.SAAS.getCode());
                        startProcessDTO.setBusinessType(XbbRefTypeEnum.PAYMENT_SHEET.getCode());
                        StartProcessVO startProcessVO = postPositionProcessHelp.startProcess(startProcessDTO);
                        if (Objects.equals(startProcessVO.getNoApprovalRequired(), 0)) {
                            // TODO 更新单据状态
                            PaymentSheetEntityExt paymentSheetEntityExt = new PaymentSheetEntityExt();
                            paymentSheetEntityExt.setCorpid(startProcessDTO.getCorpid());
                            paymentSheetEntityExt.setId(startProcessDTO.getFormDataId());
                            paymentSheetEntityExt.setFlowStatus(FlowStatusEnum.IN_APPROVAL.getType());
                            paymentSheetModel.update(paymentSheetEntityExt);
                        }*//* else if (Objects.equals(startProcessVO.getNoApprovalRequired(), 2)) {
                            // TODO 执行删除逻辑
                        }*//*
                    }
                }*/
            }
            // TODO 这里应该有点问题
            if(!Objects.equals(paymentSheetDTO.getIsImport(), 1) && Objects.nonNull(paasFormEntityExt) && Objects.equals(paasFormEntityExt.getIsProcessForm(), 1)){
                Iterator<AddMuchFieldPojo> iterator = updateBatchList.iterator();
                while (iterator.hasNext()) {
                    AddMuchFieldPojo addMuchFieldPojo = iterator.next();
                    PaymentSheetEntityExt oldPaymentSheetEntityExt = paymentSheetModel.getByKey(addMuchFieldPojo.getId(),corpid);
                    StartProcessDTO startProcessDTO = new StartProcessDTO();
                    BeanUtil.copyProperties(paymentSheetDTO, startProcessDTO, true);
                    BeanUtil.copyProperties(addMuchFieldPojo, startProcessDTO, true);
                    startProcessDTO.setFormId(paasFormEntityExt.getId());
                    startProcessDTO.setFormDataId(addMuchFieldPojo.getId());
                    startProcessDTO.setSaasMark(SaasMarkEnum.SAAS.getCode());
                    startProcessDTO.setBusinessType(XbbRefTypeEnum.PAYMENT_SHEET.getCode());
                    if (paymentSheetDTO.getProcessCreatorId() == null) {
                        startProcessDTO.setCreatorId(paymentSheetDTO.getUserId());
                    } else {
                        startProcessDTO.setCreatorId(paymentSheetDTO.getProcessCreatorId());
                    }

                    StartProcessVO startProcessVO = processStartHelp.startProcess(startProcessDTO, oldPaymentSheetEntityExt);
                    if (Objects.isNull(startProcessVO)) {
                        throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.START_PROCESS_ERROR);
                    }
                    Integer noApprovalRequired = startProcessVO.getNoApprovalRequired();
                    if ( !Objects.equals(noApprovalRequired, 1) ){
                        iterator.remove();
                    }
                }
            }

            //更新数据
            if(Objects.nonNull(updateBatchList) && !updateBatchList.isEmpty()){
                PaymentSheetUpdateBatchMuchFieldDTO paymentSheetUpdateBatchMuchFieldDTO = new PaymentSheetUpdateBatchMuchFieldDTO();
                BeanUtil.copyProperties(paymentSheetDTO, paymentSheetUpdateBatchMuchFieldDTO);
                paymentSheetUpdateBatchMuchFieldDTO.setDataList(updateBatchList);
                PaymentSheetUpdateBatchMuchFieldVO paymentSheetUpdateBatchMuchFieldVO = updateBatch(paymentSheetTypeEnum,paymentSheetUpdateBatchMuchFieldDTO, explainMap, distributorMark);
            }
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("paymentSheetServiceImpl.addBatch回款出错：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return paymentSheetAmountVO;
    }


    @Override
    public PaymentSheetAmountVO addBatchForWorkflow(PaymentSheetDTO paymentSheetDTO, PaymentSheetTypeEnum paymentSheetTypeEnum, Long customerId) throws XbbException {
        String corpid = paymentSheetDTO.getCorpid();
        String userId = paymentSheetDTO.getProcessCreatorId();

        PaymentSheetAmountVO paymentSheetAmountVO = new PaymentSheetAmountVO();
        // 批量新增流水号集合
        List<AddMuchFieldPojo> addBatchList = paymentSheetDTO.getAddBatchList();
        // 开启严控模式后生成流水号
        PaasFormExplainEntity formExplainEntity = paasFormExplainModel.getByFormId(paymentSheetDTO.getFormId(), paymentSheetDTO.getCorpid());
        if (Objects.isNull(formExplainEntity)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_EXPLAIN_NOT_EXIST);
        }
        Integer distributorMark = formExplainEntity.getDistributorMark();
        paymentSheetDTO.setDistributorMark(distributorMark);

        //获取一下回款单当前处于什么模式
        Long modelType = fundSetService.getModelType(corpid, XbbRefTypeEnum.CRM);

        Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(formExplainEntity.getExplains(), null);
        FieldAttrEntity fieldAttrEntity = explainMap.get(PaymentEnum.PAYMENT_NO.getAttr());
        if (Objects.nonNull(fieldAttrEntity)) {
            FormDataAddDTO formDataAddDTO = new FormDataAddDTO();
            BeanUtil.copyProperties(paymentSheetDTO, formDataAddDTO);
            for (AddMuchFieldPojo addMuchFieldPojo : addBatchList) {
                if (Objects.equals(fieldAttrEntity.getStrictController(), BasicConstant.ONE) &&  StringUtil.isEmpty(addMuchFieldPojo.getSerialNo())) {
                    String serialNo = "";
                    serialNo = saasSaveHelp.getSerialNo(serialNo, corpid, formDataAddDTO.getFormId(), fieldAttrEntity);
                    addMuchFieldPojo.setSerialNo(serialNo);
                }
                addMuchFieldPojo.setSysLong10(modelType);
            }
        }


        try {
            // 删除逻辑
            // 批量删除流水号集合
            List<AddMuchFieldPojo> delBatchList = paymentSheetDTO.getDelBatchList();
            if (CollectionsUtil.isNotEmpty(delBatchList)) {
                for (AddMuchFieldPojo addMuchFieldPojo : delBatchList) {
                    FormDataDeleteBatchDTO formDataDeleteBatchDTO = new FormDataDeleteBatchDTO();
                    formDataDeleteBatchDTO.setDataIdList(Collections.singletonList(addMuchFieldPojo.getId()));
                    formDataDeleteBatchDTO.setIsBusinessRule(0);
                    formDataDeleteBatchDTO.setCorpid(corpid);
                    formDataDeleteBatchDTO.setUserId(userId);
                    formDataDeleteBatchDTO.setPlatform(PlatFormEnum.INTERNAL.getValue());
                    formDataDeleteBatchDTO.setBusinessType(formExplainEntity.getBusinessType());
                    formDataDeleteBatchDTO.setFormId(formExplainEntity.getFormId());
                    formDataDeleteBatchDTO.setAppId(formExplainEntity.getAppId());
                    formDataDeleteBatchDTO.setMenuId(formExplainEntity.getMenuId());
                    formDataDeleteBatchDTO.setSaasMark(SaasMarkEnum.SAAS.getCode());
                    workflowOperatorDataService.delete(formDataDeleteBatchDTO);
                }

            }

            // 插入数据
            Double amount = 0.0D;
            if(CollectionsUtil.isNotEmpty(addBatchList)){
                for (AddMuchFieldPojo addMuchFieldPojo : addBatchList) {
                    JSONObject data = addMuchFieldPojo.getData();
                    Double realPaymentAmount = FundHelp.getSheetAmount4PaymentSheet(data, paymentSheetTypeEnum.getCode());
                    amount = Arith.add(amount, realPaymentAmount);

                    FormDataAddDTO formDataAddDTO = new FormDataAddDTO();
                    formDataAddDTO.setFromApproval(addMuchFieldPojo.getFromApproval());
                    formDataAddDTO.setAppId(formExplainEntity.getAppId());
                    formDataAddDTO.setFormId(formExplainEntity.getFormId());
                    formDataAddDTO.setMenuId(formExplainEntity.getMenuId());
                    formDataAddDTO.setSerialNo(addMuchFieldPojo.getSerialNo());
                    formDataAddDTO.setSaasMark(SaasMarkEnum.SAAS.getCode());
                    formDataAddDTO.setBusinessType(paymentSheetTypeEnum.getBusinessType());
                    formDataAddDTO.setSubBusinessType(paymentSheetTypeEnum.getBusinessType());
                    formDataAddDTO.setDataList(data);
                    formDataAddDTO.setIsBusinessRule(0);
                    formDataAddDTO.setCorpid(corpid);
                    formDataAddDTO.setUserId(userId);
                    formDataAddDTO.setPlatform(PlatFormEnum.INTERNAL.getValue());
                    JSONArray ownerJsonArray = data.getJSONArray(FieldTypeEnum.OWNERID.getAlias());
                    JSONArray coUserJsonArray = data.getJSONArray(FieldTypeEnum.COUSERID.getAlias());
                    if (Objects.nonNull(ownerJsonArray) && ownerJsonArray.size() > BasicConstant.ZERO) {
                        List<ReturnUserAndDepartmentPojo> ownerIds = ownerJsonArray.toJavaList(ReturnUserAndDepartmentPojo.class);
                        formDataAddDTO.setOwnerId(ownerIds);
                    }
                    if (Objects.nonNull(coUserJsonArray) && coUserJsonArray.size() > BasicConstant.ZERO) {
                        List<ReturnUserAndDepartmentPojo> couserIds = coUserJsonArray.toJavaList(ReturnUserAndDepartmentPojo.class);
                        formDataAddDTO.setCoUserId(couserIds);
                    }
                    workflowOperatorDataService.add(formDataAddDTO);
                }
            }
            paymentSheetAmountVO.setRealPaymentTotalAmount(amount);

            // 更新
            List<AddMuchFieldPojo> updateBatchList = paymentSheetDTO.getUpdateBatchList();
            if (CollectionsUtil.isNotEmpty(updateBatchList)) {
                UserEntity userEntity = userModel.getByKeyIngoreDel(userId, corpid);
                UserVO userVO = new UserVO();
                BeanUtil.copyProperties(userEntity, userVO);
                for (AddMuchFieldPojo addMuchFieldPojo : updateBatchList) {
                    FormDataUpdateDTO formDataUpdateDTO = new FormDataUpdateDTO(formExplainEntity.getFormId(), formExplainEntity.getAppId(), formExplainEntity.getMenuId(), addMuchFieldPojo.getId(), addMuchFieldPojo.getData(), SaasMarkEnum.SAAS.getCode(), paymentSheetTypeEnum.getBusinessType(), paymentSheetTypeEnum.getBusinessType(), null, userVO, corpid, null, PlatFormEnum.INTERNAL.getValue());
                    workflowDataService.update(formDataUpdateDTO);
                }
            }
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("com.xbongbong.saas.service.impl.PaymentSheetServiceImpl.addBatchForWorkflow 回款出错：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return paymentSheetAmountVO;

    }

    public PaymentSheetInsertBatchByContractVO insertBatch(PaymentSheetTypeEnum paymentSheetTypeEnum, PaymentSheetInsertBatchByContractDTO paymentSheetInsertBatchByContractDTO, Boolean parentNew,String userId, Long customerId) throws XbbException{
        PaymentSheetInsertBatchByContractVO paymentSheetInsertBatchByContractVO = new PaymentSheetInsertBatchByContractVO();
        try {
            long now = DateUtil.getNow();
            String corpid = paymentSheetInsertBatchByContractDTO.getCorpid();
            List<PaymentSheetEntityExt> paymentSheetEntityExtList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Double amount = 0.0D;
            Integer distributorMark = paymentSheetInsertBatchByContractDTO.getDistributorMark();
            // 记录订货单id, 后面判断订单是否用信用支付会用到
            List<Long> orderIdIn = new ArrayList<>();
            //主要用于绑定应收款与团队的信息，只有编号是唯一值，所以编号-》团队
            Map<String, AddMuchFieldPojo> serialTeamMap = new HashMap<>();
            for ( AddMuchFieldPojo item : paymentSheetInsertBatchByContractDTO.getDataList() ) {
                PaymentSheetEntityExt paymentSheetEntityExt = new PaymentSheetEntityExt();
                BeanUtil.copyProperties(item, paymentSheetEntityExt);
                JSONObject data = item.getData();
                orderIdIn = JsonHelperUtil.parseArray(JSONArray.toJSONString(data.getJSONArray(PaymentSheetEnum.CONTRACT.getAttr())), Long.class);
                fundHelp.initSheetEntity4Save(userId, paymentSheetEntityExt);
                for ( Map.Entry<String, Object> entry : SaasDataInitHelp.initPaymentSheet().entrySet() ) {
                    SaasDataInitHelp.antDataSet(data, entry.getKey(), entry.getValue());
                }
                fundHelp.removeNotInMapping(data);
                paymentSheetEntityExt.setCorpid(corpid);
                paymentSheetEntityExt.setAppId(paymentSheetInsertBatchByContractDTO.getAppId());
                paymentSheetEntityExt.setMenuId(paymentSheetInsertBatchByContractDTO.getMenuId());
                paymentSheetEntityExt.setFormId(paymentSheetInsertBatchByContractDTO.getFormId());
                // fix 24762
                if (Objects.isNull(paymentSheetEntityExt.getCreatorId())) {
                    paymentSheetEntityExt.setCreatorId(paymentSheetInsertBatchByContractDTO.getUserId());
                }

                paymentSheetEntityExt.setAddTime(now);
                paymentSheetEntityExt.setUpdateTime(now);
                //厂商端新建经销商类型回款单，且为已核销类型，则支付方式默认置为代客操作
                String sheetType = FastJsonHelper.getStringOrDefaultFromFormData(data, PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr(), "");
                saasSaveHelp.handlePayMethod4Distributor(paymentSheetInsertBatchByContractDTO.getPlatform(), data, sheetType, distributorMark);
                paymentSheetEntityExt.setData(data);
                if ( Objects.isNull(paymentSheetEntityExt.getFlowStatus()) ){
                    paymentSheetEntityExt.setFlowStatus(FlowStatusEnum.NOT_NEED_APPROVAL.getType());
                }
                paymentSheetEntityExtList.add(paymentSheetEntityExt);
                Double realPaymentAmount = FundHelp.getSheetAmount4PaymentSheet(data, paymentSheetTypeEnum.getCode());
                amount = Arith.add(amount, realPaymentAmount);
                serialTeamMap.put(item.getSerialNo(), item);
            }
            if ( Objects.equals(PaymentSheetTypeEnum.WRITE_OFF_PREPAYMENT.getCode(), paymentSheetTypeEnum.getCode()) && amount > 0 ){
                PaymentBalanceEntity paymentBalanceEntity = paymentBalanceModel.getByCustomerId(customerId,corpid);
                if ( paymentBalanceEntity == null || paymentBalanceEntity.getAdvancePaymentBalance() < amount ){
                    //todo 需要一个创建失败的消息推送
                    PushRefTypePojo.Options options = new PushRefTypePojo.Options();
                    MessageRabbitMqDTO messageRabbitMqDTO = new MessageRabbitMqDTO(corpid, Arrays.asList(paymentSheetInsertBatchByContractDTO.getUserId()), Collections.singletonList(0L), 0, null, I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_ADD_FAIL_TITLE), I18nMessageUtil.getMessage(PaymentPushConstant.PAYMENT_SHEET_ADD_FAIL_CONTENT), null, options);
                    baseProducer.sendMessage(PushTypeEnum.PAYMENT_SHEET_ADD, messageRabbitMqDTO);
                    throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208036, PaymentErrorCodeEnum.API_ERROR_208036.getMsg());
                }
                paymentBalanceEntity.setAdvancePaymentBalance(-amount);
                paymentBalanceModel.update(paymentBalanceEntity,false);
            }
            List<PaymentSheetEntityExt>  paymentSheetList = paymentSheetModel.insertBatch(paymentSheetEntityExtList);
            paymentSheetInsertBatchByContractVO.setRealPaymentTotalAmount(amount);
            paymentSheetInsertBatchByContractVO.setInsertList(paymentSheetEntityExtList);
            List<Long> paymentIdList = new ArrayList<>();
            List<Long> contractIdIn = new ArrayList<>();
            Map<Long,List<Long>> idAndContractIdMap = new HashMap<>(paymentSheetList.size());
            List<FundAccountFlowEntity> fundAccountFlowEntities = new ArrayList<>();
            Map<Long, Double> fundAccountToAmountMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            PaasFormExplainEntity paasFormExplainEntity = paasFormExplainModel.getByBusinessType4Distributor(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), corpid, distributorMark);
            Map<String, FieldAttrEntity> explainMap = paasFormExplainEntity == null ? null : ExplainUtil.getExplainMap(paasFormExplainEntity.getExplains(), PaymentSheetEnum.PAYMENT_TIME.getAttr());
            List<CreditCardBillInfoPojo> creditCardBillInfoList = new ArrayList<>();
            // 判断订货单是否信用支付
            boolean checkUseCreditFlag = creditCardBillService.checkOrderCardBill(orderIdIn, corpid);
            for (PaymentSheetEntityExt paymentEntityExt : paymentSheetList){
                Long sheetId = paymentEntityExt.getId();
                paymentIdList.add(sheetId);
                fundHelp.handleSheetIdAndContractIdMap(contractIdIn, idAndContractIdMap, paymentEntityExt, PaymentSheetEnum.CONTRACT.getAttr());
                JSONObject data = paymentEntityExt.getData();
                String type = FastJsonHelper.getStringOrDefaultFromFormData(data, PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr(), "");
                boolean writeOffFlag = Objects.equals(type, PaymentSheetTypeEnum.WRITE_OFF.getCode());
                boolean writeOffPrepaymentFlag = Objects.equals(type, PaymentSheetTypeEnum.WRITE_OFF_PREPAYMENT.getCode());
                if (writeOffFlag || writeOffPrepaymentFlag) {
                    double sheetAmount;
                    if (writeOffFlag) {
                        Long accountId = FastJsonHelper.getLongOrDefaultFromFormData(data, PaymentSheetEnum.FUND_ACCOUNT.getAttr(), 0L);
                        Long sheetTime = fundHelp.getSheetTime4FundFlow(data, PaymentSheetEnum.PAYMENT_TIME.getAttr(), explainMap, now);
                        sheetAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, PaymentSheetEnum.AMOUNT.getAttr(), 0D);
                        //账户余额变动
                        fundAccountToAmountMap.merge(accountId, sheetAmount, Arith::add);
                        //流水表日志记录
                        FundAccountFlowEntity fundAccountFlowEntity = new FundAccountFlowEntity(corpid, now, paymentEntityExt.getCreatorId(), FundAccountFlowTypeEnum.PAYMENT_SHEET.getCode(), sheetId, accountId, sheetTime, sheetAmount);
                        fundAccountFlowEntities.add(fundAccountFlowEntity);
                    } else {
                        sheetAmount = data.getDoubleValue(PaymentSheetEnum.WRITE_OFF_AMOUNT.getAttr());
                    }
                    //经销商类型回款单，则释放信用。
                    if (checkUseCreditFlag) {
                        String customerName = data.getString(PaymentSheetDistributorEnum.DISTRIBUTOR_NAME_LINK_TEXT.getAttr());
                        CreditCardBillInfoPojo infoPojo = new CreditCardBillInfoPojo(CreditCardBillTypeEnum.PAYMENT_SHEET.getCode(), sheetId, paymentEntityExt.getSerialNo(), sheetAmount, customerId, customerName, XbbRefTypeEnum.PAYMENT_SHEET.getCode());
                        creditCardBillInfoList.add(infoPojo);
                    }
                }
            }
            //按最小维度进项生成客户对账单
            saasCustomerStatementHelper.save(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), explainMap, paymentSheetList, distributorMark);
            if (fundAccountFlowEntities.size() > 0) {
                fundAccountFlowModel.insertBatch(fundAccountFlowEntities);
            }
            if (fundAccountToAmountMap.size() > 0) {
                fundAccountService.saveBatchByPaymentSheetOrPaySheet(fundAccountToAmountMap);
            }
            if (Objects.equals(distributorMark, DistributorMarkEnum.DISTRIBUTOR.getCode())) {
                if (CollectionsUtil.isNotEmpty(creditCardBillInfoList)) {
                    CreditCardBillDTO creditCardBillDTO = new CreditCardBillDTO(creditCardBillInfoList);
                    BeanUtil.copyProperties(paymentSheetInsertBatchByContractDTO, creditCardBillDTO);
                    saasCreditHelper.addBatchCreditCardBill(creditCardBillDTO);
                }
            }
            if (!Objects.equals(paymentSheetInsertBatchByContractDTO.getDistributorMark(), DistributorMarkEnum.DISTRIBUTOR.getCode())) {
                //只有普通回款单才会有团队这一层保存
                teamDataHelp.savePaymentSheetBatchBusUserTeam(paymentSheetEntityExtList, serialTeamMap, paymentSheetInsertBatchByContractDTO.getLoginUser());
            }else {
                if (parentNew){
                    List<ReturnUserAndDepartmentPojo> ownerId = paymentSheetInsertBatchByContractDTO.getOwnerId() == null ? new ArrayList<>() : paymentSheetInsertBatchByContractDTO.getOwnerId();
                    List<ReturnUserAndDepartmentPojo> coUserId = paymentSheetInsertBatchByContractDTO.getCoUserId() == null ? new ArrayList<>() : paymentSheetInsertBatchByContractDTO.getCoUserId();
                    // 审批数据和导入数据没传userName,兼容一下
                    commonHelp.setUserName4UserPojo(corpid, ownerId, coUserId);
                    List<PaymentSheetUserEntity> paymentSheetUserEntityList = new ArrayList<>();
                    for (Long paymentId : paymentIdList) {
                        for (ReturnUserAndDepartmentPojo entity : ownerId) {
                            getPaymentSheetUserTeam(customerId, contractIdIn.get(0), paymentSheetUserEntityList, paymentId, entity, BasicConstant.MAIN_USER, corpid);
                        }
                        for (ReturnUserAndDepartmentPojo entity : coUserId) {
                            getPaymentSheetUserTeam(customerId, contractIdIn.get(0), paymentSheetUserEntityList, paymentId, entity, BasicConstant.COOP_USER, corpid);
                        }
                    }
                    if (!paymentSheetUserEntityList.isEmpty()){
                        paymentSheetUserModel.insertBatch(paymentSheetUserEntityList);
                    }
                } else {
                    userTeamService.createPaymentSheetUser(corpid,customerId,contractIdIn,idAndContractIdMap,SheetTypeEnum.getByCodeRefType(paymentSheetTypeEnum.getCode(),XbbRefTypeEnum.PAYMENT_SHEET.getCode()),false);
                }
            }
        }catch (XbbException e){
            LOG.error("paymentSheetServiceImpl.insertBatch关于新建合同时关联新建" + paymentSheetTypeEnum.getName() + "失败", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        } catch (Exception e) {
            LOG.error("paymentSheetServiceImpl.insertBatch关于新建合同时关联新建" + paymentSheetTypeEnum.getName() + "失败", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return paymentSheetInsertBatchByContractVO;
    }

    public PaymentSheetUpdateBatchMuchFieldVO updateBatch(PaymentSheetTypeEnum paymentSheetTypeEnum, PaymentSheetUpdateBatchMuchFieldDTO paymentSheetUpdateBatchMuchFieldDTO, Map<String, FieldAttrEntity> explainMap, Integer distributorMark) throws XbbException{
        PaymentSheetUpdateBatchMuchFieldVO paymentSheetUpdateBatchMuchFieldVO = new PaymentSheetUpdateBatchMuchFieldVO();
        // 批量更新流水号集合
        List<AddMuchFieldPojo> dataList = paymentSheetUpdateBatchMuchFieldDTO.getDataList();
        List<String> updateBatchSerialNoList= new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        try{
            String corpid = paymentSheetUpdateBatchMuchFieldDTO.getCorpid();
            Map<String, Object> map = BeanUtil.convertBean2Map(paymentSheetUpdateBatchMuchFieldDTO, true);
            List<Long> idList = new ArrayList<>();

            List<PaasFormDataEntityExt> updateList = new ArrayList<>();
            Map<Long,AddMuchFieldPojo> dataMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            dataList.forEach((item)->{
                idList.add(item.getId());
                JSONObject jsonObject = item.getData();
                dataMap.put(item.getId(),item);
            });
            map.put("idIn", idList);
            map.put("del", DelEnum.NORMAL.getDel());
            List<PaymentSheetEntityExt> list = paymentSheetModel.findEntitys(map);
            List<Long> esDeleteList = new ArrayList<>();
            if (list == null) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_DATA_NOT_EXIST);
            } else {
                Set<Long> idSet = new HashSet<>();
                List<String> unableEditAttrList = PaymentSheetBaseEnum.getUnableEditAttrList(false, null, distributorMark);
                for(PaymentSheetEntityExt paymentSheetEntityExt: list) {
                    updateBatchSerialNoList.add(paymentSheetEntityExt.getSerialNo());
                    JSONObject dbData = paymentSheetEntityExt.getData();
                    AddMuchFieldPojo addMuchFieldPojo = dataMap.getOrDefault(paymentSheetEntityExt.getId(),new AddMuchFieldPojo());
                    for (Map.Entry<String,Object> entry : addMuchFieldPojo.getData().entrySet()){
                        //不允许编辑的字段里面没有这个字段
                        Boolean isEdit = !unableEditAttrList.contains(entry.getKey());
                        if ( isEdit ){
                            dbData.put(entry.getKey(),entry.getValue());
                        }
                    }
                    if (StringUtil.isNotEmpty(addMuchFieldPojo.getSerialNo())) {
                        paymentSheetEntityExt.setSerialNo(addMuchFieldPojo.getSerialNo());
                    }
                    fundHelp.removeNotInMapping(dbData);
                    paymentSheetEntityExt.setData(dbData);
                    updateList.add(paymentSheetEntityExt);
                    idSet.add(paymentSheetEntityExt.getId());
                }
                for(Long id : idList) {
                    if (!idSet.contains(id)) {
                        esDeleteList.add(id);
                    }
                }
            }
            // 删除数据库没有，但是es有的数据
            if (!esDeleteList.isEmpty()) {
                paasEsModel.deleteBatch(esDeleteList, corpid, DelEnum.DELETE.getDel(), IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET);
            }
            if (!updateList.isEmpty()) {
                paymentSheetHelp.updateBatch(list,paymentSheetUpdateBatchMuchFieldDTO.getCorpid(), explainMap);
            }

            // 记录日志
            String updateBatchSerialNoStr = StringUtils.join(updateBatchSerialNoList, "，");
            String userId = paymentSheetUpdateBatchMuchFieldDTO.getUserId();
            String loginUserName = paymentSheetUpdateBatchMuchFieldDTO.getLoginUserName();
            if ( dataList.size() > 1 ) {
                String memo = format(I18nMessageUtil.getMessage(LogMemoConstant.LOG_MEMO_USER_BATCH_UPDATE_FORM_DETAIL), loginUserName, XbbRefTypeEnum.PAYMENT_SHEET.getName(), dataList.size());
                List<InfoArrPojo> infoArrPojoList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                InfoArrPojo infoArrPojo = new InfoArrPojo();
                infoArrPojo.setTitle(PaymentSheetEnum.SHEET_NO.getAttrName());
                infoArrPojo.setContent(updateBatchSerialNoStr);
                infoArrPojoList.add(infoArrPojo);
                mongoLogHelp.buildLog4InfoArr(infoArrPojoList, corpid, userId, loginUserName, OperateModuleTypeEnum.PaymentManage, OperateTypeEnum.EDIT,
                        "", "", memo, paymentSheetUpdateBatchMuchFieldDTO.getHttpHeader());
            } else if ( Objects.equals(dataList.size(), 1) ) {
                String memo = format(I18nMessageUtil.getMessage(LogMemoConstant.LOG_MEMO_USER_UPDATE_WORK), loginUserName, XbbRefTypeEnum.PAYMENT_SHEET.getName(), updateBatchSerialNoStr);
                mongoLogHelp.buildLog(corpid, userId, loginUserName, OperateModuleTypeEnum.PaymentManage, OperateTypeEnum.EDIT,
                        dataList.get(0).getId().toString(), updateBatchSerialNoStr, memo, paymentSheetUpdateBatchMuchFieldDTO.getHttpHeader());
            }
        }catch (XbbException e){
            LOG.error("paymentSheetServiceImpl.updateBatch关于新建合同时关联修改" + paymentSheetTypeEnum.getName() + "失败", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return paymentSheetUpdateBatchMuchFieldVO;
    }

    /**
     * 根据关联数据id判断是否存在关联数据
     *
     * @param corpid
     * @param linkIds
     * @param attr
     * @return
     * @throws XbbException
     */
    @Override
    public Map<Long, PaasFormDataEntityExt> getEsDataMapByLinkIds(String corpid, List<Object> linkIds, PaymentSheetEnum attr) throws XbbException {
        return fundHelp.getEsDataMapByLinkIds(XbbRefTypeEnum.PAYMENT_SHEET, corpid, linkIds,attr.getAttr());
    }

    @Override
    public Map<String, Double> handleRedSheetsBySheet(String corpid, Long sheetId, AmountDetailGetPojo amountDetailGetPojo) throws XbbException {
        // 查询该应收款下面关联的所有回款单(因为这个redsheet没有约定好，这里需要重复查询)
        BoolQueryBuilder boolQueryBuilder = getBoolQueryBuilder4LinkRed(corpid, Collections.singletonList(sheetId));
        List<String> fieldList = Arrays.asList(BasicConstant.ID, BasicConstant.DATAID, FieldTypeEnum.SHEET_ALONE.getAlias(), FieldTypeEnum.SHEET_UUID.getAlias(), PaymentSheetEnum.getAttrConnectData(PaymentSheetEnum.PAYMENT_SHEET_TYPE), PaymentSheetEnum.getAttrConnectData(PaymentSheetEnum.AMOUNT), PaymentSheetEnum.getAttrConnectData(PaymentSheetEnum.WRITE_OFF_AMOUNT), PaymentSheetEnum.getAttrConnectData(PaymentSheetEnum.CONTRACT), PaymentSheetEnum.getAttrConnectData(PaymentSheetEnum.PAYMENT));
        //分页查询
        XbbAggregatedPage<PaasFormDataEntityExt> esEntities = esHelper.findByPage(IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList, BasicConstant.ONE, EsNormalConstant.FIND_NUM, null);
        List<PaasFormDataEntityExt> sheetList = new ArrayList<>();
        if (esEntities != null) {
            sheetList = esEntities.getContent();
        }
        Set<String> uuidSet = new HashSet<>();
        /**
         key：红冲回款单的合同id_应收款id
         value：红冲单据的金额
         */
        Map<String, Double> redCidPidAmountMap = new HashMap<>(sheetList.size());
        Double sumAmount = 0D;
        for (PaasFormDataEntityExt entityExt : sheetList) {
            JSONObject data = entityExt.getData();
            String paymentSheetType = FastJsonHelper.getStringFromFormData(data, PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr());
            double sheetAmount = FundHelp.getSheetAmount4PaymentSheet(data, paymentSheetType);
            sumAmount = Arith.add(sumAmount, sheetAmount);
            if (Objects.equals(entityExt.getAlone(), SheetAloneEnum.NORMAL.getCode())) {
                fundHelp.formatCidPidRedAmountMap(PaymentSheetEnum.CONTRACT.getAttr(), PaymentSheetEnum.PAYMENT.getAttr(), redCidPidAmountMap, entityExt, sheetAmount);
                continue;
            } else {
                //父单，则读取子单
                uuidSet.add(entityExt.getUuid());
            }
        }
        amountDetailGetPojo.setRedAmount(sumAmount);
        //如果有明细，则处理明细
        List<PaasFormDataEntityExt> childList = fundHelp.getChildEsDataListByParentUuids(XbbRefTypeEnum.PAYMENT_SHEET, corpid, uuidSet, null, null);
        for (PaasFormDataEntityExt entityExt : childList) {
            JSONObject data = entityExt.getData();
            String paymentSheetType = FastJsonHelper.getStringFromFormData(data, PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr());
            double sheetAmount = FundHelp.getSheetAmount4PaymentSheet(data, paymentSheetType);
            fundHelp.formatCidPidRedAmountMap(PaymentSheetEnum.CONTRACT.getAttr(), PaymentSheetEnum.PAYMENT.getAttr(), redCidPidAmountMap, entityExt, sheetAmount);
        }
        amountDetailGetPojo.setRedCidPidAmountMap(redCidPidAmountMap);
        return redCidPidAmountMap;
    }

    @Override
    public double sumRedAmountBySheet(String corpid, Long sheetId) throws XbbException {
        BoolQueryBuilder boolQueryBuilder = getBoolQueryBuilder4LinkRed(corpid, Collections.singletonList(sheetId));
        //聚合得到该条回款单（sheetId）关联的已红冲回款单总额
        //由于红冲预收款核销(退到余额)只有核销金额，因此汇总核销金额 ---  红冲预收款余额只有回款金额，但是此方法用到的地方不统计红冲预收款余额也可以
        return systemChartHelp.getDouble4Total(IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET, boolQueryBuilder, PaymentSheetEnum.getAttrConnectData(PaymentSheetEnum.WRITE_OFF_AMOUNT));
    }

    @Override
    public double sumAmountBySheet(String corpid, Long sheetId, List<String> codeList) throws XbbException {
        // 查询该应收款下面关联的所有回款单(因为这个redsheet没有约定好，这里需要重复查询)
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
        //限制alone
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.SHEET_ALONE.getAlias(), SheetAloneEnum.getNotParentCodeList()));
        boolQueryBuilder.filter(termsQuery(PaymentSheetEnum.getAttrConnectData(PaymentSheetEnum.PAYMENT), Arrays.asList(sheetId)));
        boolQueryBuilder.filter(termsQuery(PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.PAYMENT_SHEET_TYPE), codeList));
        return systemChartHelp.getDouble4Total(IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET, boolQueryBuilder, PaymentSheetEnum.getAttrConnectData(PaymentSheetEnum.AMOUNT));
    }

    @Override
    public Map<Long, Double> sumRedAmountByPaymentSheetIdIn(String corpid, List<Long> sheetIdIn) throws XbbException {
        BoolQueryBuilder boolQueryBuilder = getBoolQueryBuilder4LinkRed(corpid, sheetIdIn);
        String paymentSheetTypeAttr = PaymentSheetEnum.getAttrConnectData(PaymentSheetEnum.PAYMENT_SHEET_TYPE);
        String amountAttr = StringConstant.JSON_DATA + StringConstant.POINT + PaymentSheetEnum.AMOUNT.getAttr();
        String writeAmountAttr = PaymentSheetEnum.getAttrConnectData(PaymentSheetEnum.WRITE_OFF_AMOUNT);
        String redSheetAttr = StringConstant.JSON_DATA + StringConstant.POINT + PaymentSheetEnum.RED_SHEET.getAttr();
        List<String> fieldList = Arrays.asList(paymentSheetTypeAttr, amountAttr, writeAmountAttr, redSheetAttr);
        //分页查询
        XbbAggregatedPage<PaasFormDataEntityExt> esEntities = esHelper.findByPage(IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET, boolQueryBuilder, PaasFormDataEntityExt.class, fieldList, BasicConstant.ONE, EsNormalConstant.FIND_NUM, null);
        List<PaasFormDataEntityExt> results = new ArrayList<>();
        if (esEntities != null) {
            results = esEntities.getContent();
        }
        Map<Long, Double> sheetIdInToRedAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        results.forEach(e -> {
            JSONObject data = e.getData();
            Long dataId = data.getLong(PaymentSheetEnum.RED_SHEET.getAttr());
            Double amount = sheetIdInToRedAmount.getOrDefault(dataId, 0D);
            String paymentSheetType = data.getString(PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr());
            double sheetAmount = FundHelp.getSheetAmount4PaymentSheet(data, paymentSheetType);
            amount = Arith.add(amount, sheetAmount);
            sheetIdInToRedAmount.put(dataId, amount);
        });
        return sheetIdInToRedAmount;
    }

    @Override
    public FormDataAddDTO formatExcelValue(ImportFormDataDTO importFormDataDTO, List<CellTitlePojo> titlePojoList, RowGroupPojo rowGroupPojo, List<FieldAttrEntityForImport> fieldAttrList) throws XbbException {
        FormDataAddDTO formDataAddDTO = new FormDataAddDTO();
        BeanUtil.copyProperties(importFormDataDTO, formDataAddDTO);
        // data 数据
        JSONObject dataJson = new JSONObject();
        Long linkCustomerId = 0L;
        List<List<CellValuePojo>> rowValueList = rowGroupPojo.getRowValueList();
        // 分组的第一条数据作为给实体对象赋值的数据
        List<CellValuePojo> cellValueList = rowValueList.get(0);
        // 遍历数据
        for ( FieldAttrEntityForImport fieldAttrEntityForImport : fieldAttrList ) {
            String attr = fieldAttrEntityForImport.getAttr();
            Integer fieldType = fieldAttrEntityForImport.getFieldType();
            String attrName = fieldAttrEntityForImport.getAttrName();
            Integer columnIndex = importHelper.getColumnIndexByTitle(titlePojoList, attrName, null);
            Object cellValue = importHelper.getValueByColumnIndex(cellValueList, columnIndex);
            if (Objects.equals(attr, PaymentSheetEnum.CUSTOMER_ID.getAttr())) {
                // 关联客户根据linkedType判断
                linkCustomerId = importHelper.formatLinkCustomer2CustomerId(titlePojoList, dataJson, rowValueList, fieldAttrEntityForImport);
                if ( Objects.equals(linkCustomerId, 0L) ) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.PLEASE_ENTER, fieldAttrEntityForImport.getAttrName());
                }
            } else if ( Objects.equals(fieldType, FieldTypeEnum.SUB_FORM.getType()) ) {
                importHelper.formatSubFormValue4Import(rowValueList, titlePojoList, importFormDataDTO, fieldAttrEntityForImport, dataJson);
            } else {
                if ( Objects.isNull(cellValue) || StringUtil.isEmpty(cellValue.toString()) ) {
                    continue;
                }
                if ( ImportHelper.commonHandleFieldType.contains(fieldAttrEntityForImport.getFieldType()) ) {
                    importHelper.formatCommonField(dataJson, fieldAttrEntityForImport, cellValue);
                } else if (Objects.equals(attr, PaymentSheetEnum.CONTRACT.getAttr()) ) {
                    importHelper.formatMultiRelyBusiness(linkCustomerId, dataJson, fieldAttrEntityForImport, cellValue, ContractEnum.LINK_CUSTOMER.getAttr());
                } else if (Objects.equals(attr, PaymentSheetEnum.PAYMENT.getAttr())) {
                    importHelper.formatMultiRelyBusiness(linkCustomerId, dataJson, fieldAttrEntityForImport, cellValue, PaymentEnum.CUSTOMER.getAttr());
                } else if (Objects.equals(attr, PaymentSheetEnum.PAYMENT_TASK.getAttr())) {
                    importHelper.formatMultiRelyBusiness(linkCustomerId, dataJson, fieldAttrEntityForImport, cellValue, PaymentTaskEnum.LINK_CUSTOMER.getAttr());
                } else {
                    if ( Objects.equals(fieldType, FieldTypeEnum.SERIALNO.getType()) ) {
                        formDataAddDTO.setSerialNo(cellValue.toString());
                    } else if (Objects.equals(fieldType, FieldTypeEnum.LINK_DATA.getType())) {
                        importHelper.formatLinkData(cellValue, fieldAttrEntityForImport, dataJson);
                    } else if (Objects.equals(fieldType, FieldTypeEnum.LINK_DATA_MULTI.getType())) {
                        importHelper.formatLinkDataMulti(cellValue, importFormDataDTO, fieldAttrEntityForImport, dataJson);
                    } else {
                        dataJson.put(attr, cellValue);
                    }
                }
            }
        }
        formDataAddDTO.setDataList(dataJson);
        return formDataAddDTO;
    }

    @Override
    public void dataConsistencyUpdateCustomer(String corpid, Long customerId, String customerName) throws XbbException {
        try {
            BoolQueryBuilder filter = boolQuery()
                    .filter(termQuery("corpid.keyword", corpid))
                    .filter(termQuery("del", 0))
                    .filter(termQuery(PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.CUSTOMER_ID), customerId));
            List<String> fieldList = new ArrayList<>();
            fieldList.add(FieldTypeEnum.DATAID.getAlias());
            List<PaasFormDataEntityExt> esEntities = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET, filter, PaasFormDataEntityExt.class, fieldList);
            Set<Long> ids = new HashSet<>();
            for (PaasFormDataEntityExt esEntity : esEntities) {
                ids.add(esEntity.getDataId());
            }
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("idIn", ids);
            param.put("del", 0);
            List<PaymentSheetEntityExt> paymentSheetEntityExtList = paymentSheetModel.findEntitys(param);

            if (Objects.nonNull(paymentSheetEntityExtList) && !paymentSheetEntityExtList.isEmpty()) {
                List<PaymentSheetUpdateDTO> paymentSheetUpdateDTOList = new ArrayList<>();
                for (PaymentSheetEntityExt entityExt : paymentSheetEntityExtList) {
                    PaymentSheetUpdateDTO paymentSheetUpdateDTO = new PaymentSheetUpdateDTO();
                    paymentSheetUpdateDTO.setId(entityExt.getId());
                    JSONObject data = new JSONObject();
                    data.put(PaymentSheetEnum.CUSTOMER_NAME_LINK_TEXT.getAttr(), customerName);
                    paymentSheetUpdateDTO.setData(data);
                    paymentSheetUpdateDTOList.add(paymentSheetUpdateDTO);
                }
                PaymentSheetUpdateBatchDTO paymentSheetUpdateBatchDTO = new PaymentSheetUpdateBatchDTO();
                paymentSheetUpdateBatchDTO.setCorpid(corpid);
                paymentSheetUpdateBatchDTO.setPaymentSheetUpdateList(paymentSheetUpdateDTOList);
                updateAttrBatch(paymentSheetUpdateBatchDTO);
            }

        } catch (Exception e) {
            LOG.error("PaymentSheetServiceImpl.dataConsistencyUpdateCustomer 出错，corpid=" + corpid + "  customerId=" + customerId + " customerName=" + customerName, e);
        }
    }

    @Override
    public void dataConsistencyUpdateContract(String corpid, Long contractId, String serialNo) throws XbbException {
        try {
            BoolQueryBuilder filter = boolQuery()
                    .filter(termQuery("corpid.keyword", corpid))
                    .filter(termQuery("del", 0))
                    .filter(termQuery(PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.CONTRACT), contractId));
            List<String> fieldList = new ArrayList<>();
            fieldList.add(FieldTypeEnum.DATAID.getAlias());
            List<PaasFormDataEntityExt> esEntities = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET, filter, PaasFormDataEntityExt.class, fieldList);
            Set<Long> ids = new HashSet<>();
            for (PaasFormDataEntityExt esEntity : esEntities) {
                ids.add(esEntity.getDataId());
            }
            Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            params.put("corpid", corpid);
            params.put("idIn", ids);
            params.put("del", 0);
            List<PaymentSheetEntityExt> paymentSheetEntityExtList = paymentSheetModel.findEntitys(params);
            if (Objects.nonNull(paymentSheetEntityExtList) && !paymentSheetEntityExtList.isEmpty()) {
                List<PaymentSheetUpdateDTO> paymentSheetUpdateDTOList = new ArrayList<>();
                for (PaymentSheetEntityExt entityExt : paymentSheetEntityExtList) {
                    JSONObject oldData = entityExt.getData();
                    if (oldData == null) {
                        continue;
                    }
                    List<Long> idList = FastJsonHelper.getJsonArrOrDefaultFromFormData(oldData, PaymentSheetEnum.CONTRACT.getAttr(), new JSONArray()).toJavaList(Long.class);
                    List<String> noList = FastJsonHelper.getJsonArrOrDefaultFromFormData(oldData, PaymentSheetEnum.CONTRACT_LINK_TEXT.getAttr(), new JSONArray()).toJavaList(String.class);
                    for (int i = 0; i < idList.size(); i++) {
                        Long cid = idList.get(i);
                        if (Objects.equals(cid, contractId)) {
                            noList.set(i, serialNo);
                        }
                    }

                    PaymentSheetUpdateDTO paymentSheetUpdateDTO = new PaymentSheetUpdateDTO();
                    paymentSheetUpdateDTO.setId(entityExt.getId());
                    JSONObject data = new JSONObject();
                    data.put(PaymentSheetEnum.CONTRACT_LINK_TEXT.getAttr(), noList);
                    paymentSheetUpdateDTO.setData(data);
                    paymentSheetUpdateDTOList.add(paymentSheetUpdateDTO);
                }
                PaymentSheetUpdateBatchDTO paymentSheetUpdateBatchDTO = new PaymentSheetUpdateBatchDTO();
                paymentSheetUpdateBatchDTO.setCorpid(corpid);
                paymentSheetUpdateBatchDTO.setPaymentSheetUpdateList(paymentSheetUpdateDTOList);
                updateAttrBatch(paymentSheetUpdateBatchDTO);
            }
        } catch (Exception e) {
            LOG.error("PaymentSheetServiceImpl.dataConsistencyUpdateContract fail， corpid=" + corpid + " contractId=" + contractId + "  contractNo=" + serialNo, e);
        }
    }

    @Override
    public void updateBatchLabelAttr(AddLabelDTO addLabelDTO, List<PaasFormDataEntityExt> addBatchList, Boolean addOrDelete) throws XbbException {
        PaymentSheetUpdateBatchDTO paymentSheetUpdateBatchDTO = new PaymentSheetUpdateBatchDTO();
        BeanUtil.copyProperties(addLabelDTO, paymentSheetUpdateBatchDTO);
        List<PaymentSheetUpdateDTO> paySheetList = new ArrayList<>();
        addBatchList.forEach(item->{
            PaymentSheetUpdateDTO paymentSheetUpdateDTO= new PaymentSheetUpdateDTO();
            paymentSheetUpdateDTO.setData(item.getData());
            paymentSheetUpdateDTO.setId(item.getId());
            paySheetList.add(paymentSheetUpdateDTO);
        });
        paymentSheetUpdateBatchDTO.setPaymentSheetUpdateList(paySheetList);
        updateAttrBatch(paymentSheetUpdateBatchDTO);

    }

    /**
     * 工作流回款单团队保存
     *
     * @param saasFormSaveDTO
     */
    @Override
    public void afterSaveForWorkflow(SaasFormSaveDTO saasFormSaveDTO) throws XbbException {
        PaymentSheetEntityExt paymentSheetEntityExt = new PaymentSheetEntityExt();
        PaasFormDataEntity newPaasFormDataEntity = saasFormSaveDTO.getNewPaasFormDataEntity();
        BeanUtil.copyProperties(newPaasFormDataEntity, paymentSheetEntityExt);
        operateSheetTeam(paymentSheetEntityExt, new ArrayList<>(), true, newPaasFormDataEntity, saasFormSaveDTO.getSaasNeedRedundantAttrPoJo(), saasFormSaveDTO.getIsImport(), true);
    }

    /**
     * 判断前端输入的预收款核销、红冲预收款余额类型回款单的金额是否超过客户余额
     * @param dataList 回款单data
     * @param corpid 公司id
     * @param sheetAmount 回款单金额（红冲预收款余额的红冲金额、预收款核销的核销金额）
     * @throws XbbException
     * @author zcp
     * @date 19/12/10 010 15:42
     * @update 19/12/10 010 15:42
     * @since v1.0
     * @version v1.0
     */
    private void checkCustomerBalance(JSONObject dataList, String corpid, Double sheetAmount) throws XbbException {
        //预收款核销，要控制金额小于客户余额
        Long customerId = FastJsonHelper.getLongOrDefaultFromFormData(dataList, PaymentSheetEnum.CUSTOMER_ID.getAttr(), 0L);
        PaymentBalanceEntity paymentBalanceEntity = paymentBalanceModel.getByCustomerId(customerId, corpid);
        //如果客户预收款余额不足，则不能保存成功
        boolean nonEmptyFlag = paymentBalanceEntity != null && paymentBalanceEntity.getAdvancePaymentBalance() != null;
        boolean over = nonEmptyFlag && Arith.sub(sheetAmount , paymentBalanceEntity.getAdvancePaymentBalance()) > ConfigConstant.amountAccuracy;
        if (over) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.DETAIL_BEYOND_BALANCE_RED);
        }
    }

    /**
     * 得到回款单可回款（可红冲、可坏账）金额
     * 回款、坏账：获取合同/应收款的未收
     * 红冲：获取（红冲回款单关联的合同/应收款的）已收
     * @param paymentSheetSaveDTO 回款单保存dto
     * @param contractIds 创建的回款单关联的合同id集合
     * @param paymentIds 创建的回款单关联的回款id集合
     * @param linkPreId 关联原始回款单id
     * @return java.lang.Double
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private Double getSumAmount(PaymentSheetSaveDTO paymentSheetSaveDTO, Set<Long> contractIds, Set<Long> paymentIds, Long linkPreId) throws XbbException {
        SheetAmountDetailGetDTO sheetAmountDetailGetDTO = new SheetAmountDetailGetDTO();
        BeanUtil.copyProperties(paymentSheetSaveDTO, sheetAmountDetailGetDTO);
        sheetAmountDetailGetDTO.setDataIdList(new ArrayList<>(contractIds));
        //空的数组也作为筛选条件
        sheetAmountDetailGetDTO.setEmptyFilterFlag(true);
        sheetAmountDetailGetDTO.setPaymentIdList(new ArrayList<>(paymentIds));
        sheetAmountDetailGetDTO.setLinkPreId(linkPreId);
        SheetAmountDetailGetVO sheetAmountDetailGetVO = paymentService.paymentAmountDetailGet(true, sheetAmountDetailGetDTO);
        return sheetAmountDetailGetVO.getSumAmount();
    }

    /**
     * 原始回款单的关联红冲回款单的查询条件
     * @param corpid 公司id
     * @param sheetIdIn 原单id集合
     * @return org.elasticsearch.index.query.BoolQueryBuilder
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private BoolQueryBuilder getBoolQueryBuilder4LinkRed(String corpid, List<Long> sheetIdIn) {
        // 查询该应收款下面关联的所有回款单(因为这个redsheet没有约定好，这里需要重复查询)
        BoolQueryBuilder boolQueryBuilder = boolQuery();
        boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
        //限制alone
        boolQueryBuilder.filter(termsQuery(FieldTypeEnum.SHEET_ALONE.getAlias(), SheetAloneEnum.getNotChildCodeList()));
        //限制关联原始回款单id为sheetId
        boolQueryBuilder.filter(termsQuery(PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.RED_SHEET), sheetIdIn));
        //只读红冲类型回款单
        boolQueryBuilder.filter(termsQuery(PaymentSheetEnum.getEsAttr4Keyword(PaymentSheetEnum.PAYMENT_SHEET_TYPE), PaymentSheetTypeEnum.getRedCodeList()));
        return boolQueryBuilder;
    }

    /**
     * 为新建已核销、预收款核销、红冲已核销、红冲预收款核销、坏账5种类型的回款单增加项目日志、消息推送
     * @param pushTypeEnum 推送类型枚举
     * @param dataList 回款单data
     * @param corpid 公司id
     * @param userId 登录员工
     * @param paymentSheetEntityExt 回款单实体
     * @param logMemo 项目日志内容，@see CustomerCommunicateConstant
     * @param pushTitle 推送标题 @see PaymentPushConstant
     * @param pushContent 推送内容 @see PaymentPushConstant
     * @param distributorMark 经销商标识
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void createLogAndMessage(PushTypeEnum pushTypeEnum, JSONObject dataList, String corpid, String userId, PaymentSheetEntityExt paymentSheetEntityExt, String logMemo, String pushTitle, String pushContent, Integer distributorMark) throws XbbException {
        String contractTitle = XbbRefTypeEnum.CONTRACT.getName();
        if (Objects.equals(distributorMark, DistributorMarkEnum.DISTRIBUTOR.getCode())) {
            contractTitle = XbbRefTypeEnum.ORDER.getName();
        }
        // 添加项目日志
        JSONArray contractNoArr = dataList.getJSONArray(PaymentSheetEnum.CONTRACT_LINK_TEXT.getAttr());
        List<String> contractNoList = contractNoArr == null ? new ArrayList<>() : contractNoArr.toJavaList(String.class);
        //逗号分隔的合同编号字符串
        String contractNos = join(SymbolConstant.COMMA_ENGLISH, contractNoList);
        //logMemo需要拼上合同编号
        if (Objects.equals(distributorMark, DistributorMarkEnum.DISTRIBUTOR.getCode())) {
            String communicateMemo = format(logMemo, contractTitle, contractNos);
            CustomerDynamicAddDTO customerDynamicAddDTO = new CustomerDynamicAddDTO();
            customerDynamicAddDTO.setCorpid(corpid);
            customerDynamicAddDTO.setUserId(userId);
            customerDynamicAddDTO.setCustomerId(StringUtil.StringToLong(dataList.getString(PaymentSheetEnum.CUSTOMER_ID.getAttr())));
            customerDynamicAddDTO.setCustomerName(dataList.getString(PaymentSheetEnum.CUSTOMER_NAME_LINK_TEXT.getAttr()));
            customerDynamicAddDTO.setMemo(communicateMemo);
            customerDynamicAddDTO.setCommunicateBusinessText(paymentSheetEntityExt.getSerialNo());
            customerDynamicAddDTO.setCommunicateBusinessType(XbbRefTypeEnum.PAYMENT_SHEET.getCode());
            customerDynamicAddDTO.setDataId(paymentSheetEntityExt.getId());
            customerDynamicAddDTO.setLogType(ProjectLogEnum.PAYMENT_CREATE.getSubType());
            customerDynamicAddDTO.setDistributorMark(distributorMark);
            customerCommunicateService.addCustomerDynamic(customerDynamicAddDTO);
        }
        // 回款单创建消息推送
        Long dataId = paymentSheetEntityExt.getId();
        UserEntity userEntity = userModel.getByKeyIngoreDel(paymentSheetEntityExt.getCreatorId(), paymentSheetEntityExt.getCorpid());
        if (Objects.nonNull(userEntity)) {
            List<String> pushUserList = pushHelper.getUserIdListByPushSetRule(corpid, pushTypeEnum.getCode(), pushTypeEnum.getSubCode(), userEntity, paymentSheetEntityExt);
            PushRefTypePojo.Options options = new PushRefTypePojo.Options(paymentSheetEntityExt.getAppId(), paymentSheetEntityExt.getMenuId(), paymentSheetEntityExt.getFormId(), dataId, Collections.singletonList(dataId), SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.PAYMENT_SHEET.getCode(), XbbRefTypeEnum.PAYMENT_SHEET.getCode());
            MessageRabbitMqDTO messageRabbitMqDTO = new MessageRabbitMqDTO(corpid, pushUserList, Collections.singletonList(dataId), 0, null, pushTitle, format(pushContent, userEntity.getName(), contractNos), null, options);
            baseProducer.sendMessage(pushTypeEnum, messageRabbitMqDTO);
        }
    }

    /**
     * 为新建预收款、红冲预收款余额2种类型的回款单增加项目日志、消息推送
     * @param xbbRefTypeEnum 类型
     * @param pushTypeEnum 推送类型枚举
     * @param dataList 回款单data
     * @param corpid 公司id
     * @param userId 登录员工
     * @param paymentSheetEntityExt 回款单实体
     * @param logMemo 项目日志内容，@see CustomerCommunicateConstant
     * @param pushTitle 推送标题 @see PaymentPushConstant
     * @param pushContent 推送内容 @see PaymentPushConstant
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void createLogAndMessage4Pre(XbbRefTypeEnum xbbRefTypeEnum, PushTypeEnum pushTypeEnum, JSONObject dataList, String corpid, String userId, PaymentSheetEntityExt paymentSheetEntityExt, String logMemo, String pushTitle, String pushContent, Integer distributorMark) throws XbbException {
        String customerTitle = XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getName();
        if (Objects.equals(distributorMark, DistributorMarkEnum.DISTRIBUTOR.getCode())) {
            customerTitle = XbbRefTypeEnum.DISTRIBUTOR_MANAGEMENT.getName();
        }
        Long customerId = StringUtil.StringToLong(dataList.getString(PaymentSheetEnum.CUSTOMER_ID.getAttr()));
        String customerName = dataList.getString(PaymentSheetEnum.CUSTOMER_NAME_LINK_TEXT.getAttr());
        if(Objects.equals(distributorMark,DistributorMarkEnum.DISTRIBUTOR.getCode())) {
            logMemo = String.format(logMemo, customerTitle, customerName);
            CustomerDynamicAddDTO customerDynamicAddDTO = new CustomerDynamicAddDTO();
            customerDynamicAddDTO.setCorpid(corpid);
            customerDynamicAddDTO.setUserId(userId);
            customerDynamicAddDTO.setCustomerId(customerId);
            customerDynamicAddDTO.setCustomerName(customerName);
            customerDynamicAddDTO.setMemo(logMemo);
            customerDynamicAddDTO.setDataId(paymentSheetEntityExt.getId());
            customerDynamicAddDTO.setLogType(ProjectLogEnum.PAYMENT_CREATE.getSubType());
            customerDynamicAddDTO.setCommunicateBusinessText(paymentSheetEntityExt.getSerialNo());
            //预收款打开也要用回款单的702
            customerDynamicAddDTO.setCommunicateBusinessType(XbbRefTypeEnum.PAYMENT_SHEET.getCode());
            customerDynamicAddDTO.setDistributorMark(distributorMark);
            customerCommunicateService.addCustomerDynamic(customerDynamicAddDTO);
        }
        // 回款单创建消息推送
        Long dataId = paymentSheetEntityExt.getId();
        UserEntity userEntity = userModel.getByKeyIngoreDel(paymentSheetEntityExt.getCreatorId(), paymentSheetEntityExt.getCorpid());
        if (Objects.nonNull(userEntity)) {
            List<String> pushUserList = pushHelper.getUserIdListByPushSetRule(corpid, pushTypeEnum.getCode(), pushTypeEnum.getSubCode(), userEntity, paymentSheetEntityExt);
            PushRefTypePojo.Options options = new PushRefTypePojo.Options(paymentSheetEntityExt.getAppId(), paymentSheetEntityExt.getMenuId(), paymentSheetEntityExt.getFormId(), dataId, Collections.singletonList(dataId), SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.PAYMENT_SHEET.getCode(), XbbRefTypeEnum.PAYMENT_SHEET.getCode());
            MessageRabbitMqDTO messageRabbitMqDTO = new MessageRabbitMqDTO(corpid, pushUserList, Collections.singletonList(dataId), 0, null, pushTitle, format(pushContent, userEntity.getName(), customerName), null, options);
            baseProducer.sendMessage(pushTypeEnum, messageRabbitMqDTO);
        }
    }

}
